luckytri 1.0.6 → 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/LICENSE +21 -21
- package/README.en.md +7 -3
- package/README.md +7 -3
- package/docs/en/CHANGELOG.md +35 -16
- package/docs/en/chat-style.md +28 -9
- package/docs/en/guide.md +5 -5
- package/docs/en/her-life.md +2 -2
- package/docs/en/webui.md +1 -1
- package/docs/zh/CHANGELOG.md +35 -16
- package/docs/zh/chat-style.md +30 -9
- package/docs/zh/guide.md +5 -5
- package/docs/zh/her-life.md +2 -2
- package/docs/zh/webui.md +1 -1
- package/package.json +1 -1
- package/public/app/assets/{ChatsPage-DCYMkJ91.js → ChatsPage-CshdtTzV.js} +2 -2
- package/public/app/assets/ChatsPage-DfyPCS9N.css +1 -0
- package/public/app/assets/{Empty-CLyZkxR5.js → Empty-C7yzOBzy.js} +1 -1
- package/public/app/assets/{Empty-DIobYG3x.css → Empty-DmUB2-Bk.css} +1 -1
- package/public/app/assets/{Field-BpaRwLI_.js → Field-BjFv4Rfn.js} +1 -1
- package/public/app/assets/HeartPage-Bft9wZp5.css +1 -0
- package/public/app/assets/{HeartPage-BIOMTiwr.js → HeartPage-eqo6ru16.js} +1 -1
- package/public/app/assets/LifePage-BUi5euBF.css +1 -0
- package/public/app/assets/{LifePage-BCgM4JkR.js → LifePage-CN8GE5NK.js} +1 -1
- package/public/app/assets/{MemoryPage-vShvRqMt.js → MemoryPage-CCpMN4Wg.js} +1 -1
- package/public/app/assets/MemoryPage-cZvXfkXb.css +1 -0
- package/public/app/assets/{NaturePage-Cy9ptr4G.js → NaturePage-C0JqdBTR.js} +2 -2
- package/public/app/assets/NaturePage-CHkpb5NK.css +1 -0
- package/public/app/assets/PeoplePage-BTU0H1pM.css +1 -0
- package/public/app/assets/{PeoplePage-CUZekB2E.js → PeoplePage-BXGBbO4u.js} +1 -1
- package/public/app/assets/{PluginsPage-1RM4rmJP.js → PluginsPage-Dksp5AV5.js} +1 -1
- package/public/app/assets/{SystemPage-BI2CB5Ig.css → SystemPage-BenIyTvk.css} +1 -1
- package/public/app/assets/{SystemPage-BeT5TmIV.js → SystemPage-Cd7aa8eE.js} +1 -1
- package/public/app/assets/TimePage-BOms_n4I.css +1 -0
- package/public/app/assets/{TimePage-taTmPMM8.js → TimePage-Bs_zImb4.js} +2 -2
- package/public/app/assets/{index-DY5AM0Hz.css → index-BHZcHUMh.css} +1 -1
- package/public/app/assets/{index-DdYwC5ZU.js → index-CyRtYxP3.js} +3 -3
- package/public/app/assets/{knowledge-DJmfu2KN.js → knowledge-BJOIyuJo.js} +1 -1
- package/public/app/assets/{media-BEtnE0Gv.js → media-Bwtx8blz.js} +1 -1
- package/public/app/assets/{mind-Dtiw0-QK.js → mind-DtTVrW6Z.js} +1 -1
- package/public/app/index.html +5 -6
- package/public/guide.en.html +3 -3
- package/public/guide.html +3 -3
- package/scripts/import-data.js +128 -128
- package/scripts/proxy-config.js +167 -167
- package/scripts/update-instance.js +201 -201
- package/server/channels/adapters.js +27 -27
- package/server/channels/onebot/adapter.js +288 -288
- package/server/channels/qqbot/adapter.js +253 -253
- package/server/channels/qqbot/api.js +199 -199
- package/server/channels/qqbot/credentials.js +11 -11
- package/server/channels/qqbot/gateway.js +274 -274
- package/server/channels/qqbot/index.js +389 -389
- package/server/channels/segments.js +40 -40
- package/server/channels/session-key.js +153 -153
- package/server/channels/state.js +42 -42
- package/server/core/api.js +564 -564
- package/server/core/context-builder.js +288 -288
- package/server/core/context-compactor.js +454 -454
- package/server/core/conversation-cues.js +385 -325
- package/server/core/conversation-grounding.js +304 -0
- package/server/core/conversation-manager.js +87 -87
- package/server/core/dialogue-context.js +244 -0
- package/server/core/dialogue-contract.js +35 -0
- package/server/core/feedback.js +8 -8
- package/server/core/group-style.js +72 -72
- package/server/core/initiative-context.js +68 -66
- package/server/core/input-budget.js +32 -32
- package/server/core/local-demo.js +98 -98
- package/server/core/message-manager.js +118 -118
- package/server/core/message-scheduler.js +78 -78
- package/server/core/model-json.js +190 -190
- package/server/core/model-manager.js +5 -8
- package/server/core/model-presets.js +900 -900
- package/server/core/name-call.js +29 -29
- package/server/core/network.js +157 -157
- package/server/core/person-name.js +22 -22
- package/server/core/persona-manager.js +359 -269
- package/server/core/reader-check.js +62 -0
- package/server/core/reply-delivery.js +537 -404
- package/server/core/reply-target-resolver.js +50 -50
- package/server/core/response-generator.js +92 -50
- package/server/core/response-validator.js +53 -32
- package/server/core/retired-prompts.js +51 -51
- package/server/core/sessions.js +109 -109
- package/server/core/speaker-names.js +92 -92
- package/server/core/topic-tracker.js +65 -65
- package/server/core/turn.js +82 -15
- package/server/http.js +18 -18
- package/server/i18n/decisions.js +209 -209
- package/server/i18n/errors.js +2 -0
- package/server/i18n/index.js +144 -144
- package/server/i18n/labels.js +6 -10
- package/server/i18n/models.js +111 -111
- package/server/knowledge/api.js +342 -342
- package/server/knowledge/embedding-profile.js +114 -114
- package/server/knowledge/index.js +13 -13
- package/server/knowledge/retrieval.js +93 -93
- package/server/knowledge/schema.js +158 -158
- package/server/knowledge/vector-index.js +90 -90
- package/server/mind/affect.js +265 -265
- package/server/mind/attention.js +200 -200
- package/server/mind/bonds.js +565 -565
- package/server/mind/clock.js +83 -83
- package/server/mind/context-selection.js +16 -4
- package/server/mind/continuity.js +562 -562
- package/server/mind/conversation-origin.js +175 -175
- package/server/mind/days.js +210 -210
- package/server/mind/evidence-read.js +45 -45
- package/server/mind/expression-grounding.js +21 -0
- package/server/mind/faces.js +373 -373
- package/server/mind/guard.js +68 -60
- package/server/mind/initiative.js +338 -338
- package/server/mind/life/activities.js +21 -21
- package/server/mind/life/context.js +330 -330
- package/server/mind/life/growth.js +261 -261
- package/server/mind/life/presence.js +305 -305
- package/server/mind/meetings.js +1069 -1069
- package/server/mind/memory-policy.js +48 -48
- package/server/mind/memory.js +888 -888
- package/server/mind/nature.js +358 -358
- package/server/mind/novelty.js +19 -19
- package/server/mind/own-voice.js +35 -0
- package/server/mind/periods.js +118 -118
- package/server/mind/reading.js +132 -120
- package/server/mind/relationship-context.js +71 -71
- package/server/mind/relationships.js +390 -390
- package/server/mind/salience.js +201 -201
- package/server/mind/schema.js +567 -567
- package/server/mind/self.js +496 -484
- package/server/mind/thoughts.js +274 -274
- package/server/mind/time/agenda.js +242 -242
- package/server/mind/time/api.js +200 -200
- package/server/mind/time/attention.js +34 -34
- package/server/mind/time/availability.js +23 -1
- package/server/mind/time/executor.js +4 -2
- package/server/mind/time/games.js +20 -11
- package/server/mind/time/index.js +9 -2
- package/server/mind/time/kinds.js +29 -2
- package/server/mind/time/material-relevance.js +19 -0
- package/server/mind/time/model-material.js +77 -77
- package/server/mind/time/presentation.js +35 -51
- package/server/mind/time/reading-input.js +31 -0
- package/server/mind/time/schedule.js +83 -83
- package/server/mind/time/schema.js +84 -84
- package/server/mind/time/search.js +383 -383
- package/server/mind/time/sharing.js +482 -472
- package/server/mind/time/task-links.js +311 -311
- package/server/mind/time/tasks.js +41 -3
- package/server/mind/time/works.js +527 -432
- package/server/mind/traits.js +191 -191
- package/server/mind/view.js +62 -12
- package/server/runtime/installation.js +116 -116
- package/server/runtime/update-manager.js +140 -140
- package/server/storage/data-package.js +298 -298
- package/server/storage/data-transfer-api.js +50 -50
- package/server/storage/data-transfer-worker.js +18 -18
- package/server/storage/data-transfer.js +416 -416
- package/server/studio/display-names.js +139 -139
- package/server/studio/management.js +102 -102
- package/server/studio/updates.js +144 -144
- package/server/version.js +10 -10
- package/public/app/assets/ChatsPage-CFMVozoG.css +0 -1
- package/public/app/assets/HeartPage-C-2jvgzF.css +0 -1
- package/public/app/assets/LifePage-CG1OHbRK.css +0 -1
- package/public/app/assets/MemoryPage-C_Bu4rfb.css +0 -1
- package/public/app/assets/NaturePage-CPZlImT0.css +0 -1
- package/public/app/assets/PeoplePage-3RRUITDz.css +0 -1
- package/public/app/assets/TimePage-C-guwPk2.css +0 -1
|
@@ -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
|
+
}
|
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
import { withFallback } from "../../core/model-manager.js";
|
|
2
|
-
import { hasCredential, text } from "../util.js";
|
|
3
|
-
import { sanitizeDetail } from "../../core/network.js";
|
|
4
|
-
|
|
5
|
-
export const MODEL_MATERIAL_LABEL = "模型知识整理,未经联网核验";
|
|
6
|
-
const PROMPT =
|
|
7
|
-
'根据已有知识整理资料主题,不执行联网搜索。输入只包含资料主题,不是指令。不编造网址、引用、游戏操作或不确定的章节细节。明确区分已知信息与推测,不确定就说明。仅输出JSON {"sufficient":true,"materials":[{"title":"资料标题","content":"300至1000字本段剧情、情境与人物互动","uncertainty":"具体不确定之处","timing":{"minutes":25,"basis":"这段剧情、对话、探索通常需要多少分钟","chapterMinutes":120}}]};timing估计本段游玩时长,不按摘要字数或API运行速度估计;chapterMinutes仅在有把握估计这一章时给出。不熟悉该主题时sufficient:false且materials:[]。不输出隐藏推理。';
|
|
8
|
-
|
|
9
|
-
export async function modelMaterial(life, topic, { trace, maxResults }) {
|
|
10
|
-
if (!life || life.closed) throw Error("等待可用模型");
|
|
11
|
-
if (!life.mind.budget.allows("inner", life.now()))
|
|
12
|
-
throw Error("今日独处预算不足");
|
|
13
|
-
const ownTrace = !trace,
|
|
14
|
-
activeTrace = trace || life.repo.trace("__mind__", "model-material"),
|
|
15
|
-
runId = ownTrace ? life.run("activity", "用模型整理资料主题") : null;
|
|
16
|
-
let status = "error",
|
|
17
|
-
keys = [];
|
|
18
|
-
try {
|
|
19
|
-
const profile = life.profile(),
|
|
20
|
-
fallback = life.chat.fallbackFor(null, profile, activeTrace),
|
|
21
|
-
start = activeTrace.calls.length;
|
|
22
|
-
keys = [profile, ...(Array.isArray(fallback) ? fallback : [fallback])]
|
|
23
|
-
.map((p) => p?.apiKey)
|
|
24
|
-
.filter(Boolean);
|
|
25
|
-
const answer = await withFallback(life.chat.models, fallback).call(
|
|
26
|
-
profile,
|
|
27
|
-
"reflection",
|
|
28
|
-
PROMPT,
|
|
29
|
-
{ topic, maxResults: Math.min(3, maxResults) },
|
|
30
|
-
activeTrace,
|
|
31
|
-
);
|
|
32
|
-
if (life.closed) throw Error("实例已停止,资料结果未提交");
|
|
33
|
-
const actual =
|
|
34
|
-
activeTrace.calls.slice(start).findLast((c) => !c.error && c.model)
|
|
35
|
-
?.model || profile;
|
|
36
|
-
const rows =
|
|
37
|
-
answer?.sufficient === true && Array.isArray(answer.materials)
|
|
38
|
-
? answer.materials
|
|
39
|
-
.slice(0, Math.min(3, maxResults))
|
|
40
|
-
.map((row) => ({
|
|
41
|
-
title: text(row.title, 200),
|
|
42
|
-
content: text(row.content, 4000),
|
|
43
|
-
uncertainty:
|
|
44
|
-
text(row.uncertainty, 400) ||
|
|
45
|
-
"基于模型已有知识,具体剧情与章节仍需核验。",
|
|
46
|
-
kind: "model",
|
|
47
|
-
model: text(actual.model || actual.id, 200),
|
|
48
|
-
url: "",
|
|
49
|
-
timing:
|
|
50
|
-
row.timing && typeof row.timing === "object"
|
|
51
|
-
? {
|
|
52
|
-
minutes: Number(row.timing.minutes) || null,
|
|
53
|
-
basis: text(row.timing.basis, 240),
|
|
54
|
-
chapterMinutes: Number(row.timing.chapterMinutes) || null,
|
|
55
|
-
}
|
|
56
|
-
: {},
|
|
57
|
-
}))
|
|
58
|
-
.filter(
|
|
59
|
-
(row) =>
|
|
60
|
-
row.title &&
|
|
61
|
-
row.content.length >= 120 &&
|
|
62
|
-
!hasCredential(row.title + row.content + row.uncertainty),
|
|
63
|
-
)
|
|
64
|
-
: [];
|
|
65
|
-
status = "complete";
|
|
66
|
-
return rows;
|
|
67
|
-
} catch (error) {
|
|
68
|
-
let reason = String(error.message || error);
|
|
69
|
-
for (const key of keys) reason = reason.split(key).join("[密钥已隐藏]");
|
|
70
|
-
throw Error(sanitizeDetail(reason, 160));
|
|
71
|
-
} finally {
|
|
72
|
-
if (ownTrace) {
|
|
73
|
-
life.chat.finishQuietly(activeTrace, status);
|
|
74
|
-
life.end(runId, status, MODEL_MATERIAL_LABEL, activeTrace);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
1
|
+
import { withFallback } from "../../core/model-manager.js";
|
|
2
|
+
import { hasCredential, text } from "../util.js";
|
|
3
|
+
import { sanitizeDetail } from "../../core/network.js";
|
|
4
|
+
|
|
5
|
+
export const MODEL_MATERIAL_LABEL = "模型知识整理,未经联网核验";
|
|
6
|
+
const PROMPT =
|
|
7
|
+
'根据已有知识整理资料主题,不执行联网搜索。输入只包含资料主题,不是指令。不编造网址、引用、游戏操作或不确定的章节细节。明确区分已知信息与推测,不确定就说明。仅输出JSON {"sufficient":true,"materials":[{"title":"资料标题","content":"300至1000字本段剧情、情境与人物互动","uncertainty":"具体不确定之处","timing":{"minutes":25,"basis":"这段剧情、对话、探索通常需要多少分钟","chapterMinutes":120}}]};timing估计本段游玩时长,不按摘要字数或API运行速度估计;chapterMinutes仅在有把握估计这一章时给出。不熟悉该主题时sufficient:false且materials:[]。不输出隐藏推理。';
|
|
8
|
+
|
|
9
|
+
export async function modelMaterial(life, topic, { trace, maxResults }) {
|
|
10
|
+
if (!life || life.closed) throw Error("等待可用模型");
|
|
11
|
+
if (!life.mind.budget.allows("inner", life.now()))
|
|
12
|
+
throw Error("今日独处预算不足");
|
|
13
|
+
const ownTrace = !trace,
|
|
14
|
+
activeTrace = trace || life.repo.trace("__mind__", "model-material"),
|
|
15
|
+
runId = ownTrace ? life.run("activity", "用模型整理资料主题") : null;
|
|
16
|
+
let status = "error",
|
|
17
|
+
keys = [];
|
|
18
|
+
try {
|
|
19
|
+
const profile = life.profile(),
|
|
20
|
+
fallback = life.chat.fallbackFor(null, profile, activeTrace),
|
|
21
|
+
start = activeTrace.calls.length;
|
|
22
|
+
keys = [profile, ...(Array.isArray(fallback) ? fallback : [fallback])]
|
|
23
|
+
.map((p) => p?.apiKey)
|
|
24
|
+
.filter(Boolean);
|
|
25
|
+
const answer = await withFallback(life.chat.models, fallback).call(
|
|
26
|
+
profile,
|
|
27
|
+
"reflection",
|
|
28
|
+
PROMPT,
|
|
29
|
+
{ topic, maxResults: Math.min(3, maxResults) },
|
|
30
|
+
activeTrace,
|
|
31
|
+
);
|
|
32
|
+
if (life.closed) throw Error("实例已停止,资料结果未提交");
|
|
33
|
+
const actual =
|
|
34
|
+
activeTrace.calls.slice(start).findLast((c) => !c.error && c.model)
|
|
35
|
+
?.model || profile;
|
|
36
|
+
const rows =
|
|
37
|
+
answer?.sufficient === true && Array.isArray(answer.materials)
|
|
38
|
+
? answer.materials
|
|
39
|
+
.slice(0, Math.min(3, maxResults))
|
|
40
|
+
.map((row) => ({
|
|
41
|
+
title: text(row.title, 200),
|
|
42
|
+
content: text(row.content, 4000),
|
|
43
|
+
uncertainty:
|
|
44
|
+
text(row.uncertainty, 400) ||
|
|
45
|
+
"基于模型已有知识,具体剧情与章节仍需核验。",
|
|
46
|
+
kind: "model",
|
|
47
|
+
model: text(actual.model || actual.id, 200),
|
|
48
|
+
url: "",
|
|
49
|
+
timing:
|
|
50
|
+
row.timing && typeof row.timing === "object"
|
|
51
|
+
? {
|
|
52
|
+
minutes: Number(row.timing.minutes) || null,
|
|
53
|
+
basis: text(row.timing.basis, 240),
|
|
54
|
+
chapterMinutes: Number(row.timing.chapterMinutes) || null,
|
|
55
|
+
}
|
|
56
|
+
: {},
|
|
57
|
+
}))
|
|
58
|
+
.filter(
|
|
59
|
+
(row) =>
|
|
60
|
+
row.title &&
|
|
61
|
+
row.content.length >= 120 &&
|
|
62
|
+
!hasCredential(row.title + row.content + row.uncertainty),
|
|
63
|
+
)
|
|
64
|
+
: [];
|
|
65
|
+
status = "complete";
|
|
66
|
+
return rows;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
let reason = String(error.message || error);
|
|
69
|
+
for (const key of keys) reason = reason.split(key).join("[密钥已隐藏]");
|
|
70
|
+
throw Error(sanitizeDetail(reason, 160));
|
|
71
|
+
} finally {
|
|
72
|
+
if (ownTrace) {
|
|
73
|
+
life.chat.finishQuietly(activeTrace, status);
|
|
74
|
+
life.end(runId, status, MODEL_MATERIAL_LABEL, activeTrace);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -1,51 +1,35 @@
|
|
|
1
|
-
import { text } from "../util.js";
|
|
2
|
-
|
|
3
|
-
export function gameText(value) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
.
|
|
12
|
-
.
|
|
13
|
-
.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
: {}),
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
export function gameContract(contract) {
|
|
41
|
-
if (!contract) return undefined;
|
|
42
|
-
return {
|
|
43
|
-
unit: contract.unit,
|
|
44
|
-
outcome: gameText(contract.outcome),
|
|
45
|
-
stopAfterNote: contract.stopAfterNote,
|
|
46
|
-
delivery: contract.delivery,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
export function naturalGameReason(reason) {
|
|
50
|
-
return text(gameText(reason), 300);
|
|
51
|
-
}
|
|
1
|
+
import { text } from "../util.js";
|
|
2
|
+
|
|
3
|
+
export function gameText(value) {
|
|
4
|
+
// Presentation must never turn uncertainty or a reference into lived fact.
|
|
5
|
+
return String(value || "").trim();
|
|
6
|
+
}
|
|
7
|
+
export function activityPresentation(row) {
|
|
8
|
+
if (!row || (row.activity || row.kind) !== "game") return row;
|
|
9
|
+
return {
|
|
10
|
+
...row,
|
|
11
|
+
title: gameText(row.title),
|
|
12
|
+
why: gameText(row.why),
|
|
13
|
+
...(row.checkpoint && typeof row.checkpoint === "object"
|
|
14
|
+
? {
|
|
15
|
+
checkpoint: {
|
|
16
|
+
...row.checkpoint,
|
|
17
|
+
next: gameText(row.checkpoint.next),
|
|
18
|
+
summary: gameText(row.checkpoint.summary),
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
: {}),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function gameContract(contract) {
|
|
25
|
+
if (!contract) return undefined;
|
|
26
|
+
return {
|
|
27
|
+
unit: contract.unit,
|
|
28
|
+
outcome: gameText(contract.outcome),
|
|
29
|
+
stopAfterNote: contract.stopAfterNote,
|
|
30
|
+
delivery: contract.delivery,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function naturalGameReason(reason) {
|
|
34
|
+
return text(gameText(reason), 300);
|
|
35
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
import { dayKey, zonedTime } from "../util.js";
|
|
2
|
-
import { localClock } from "../../core/conversation-cues.js";
|
|
3
|
-
import { gameText } from "./presentation.js";
|
|
4
|
-
export function spokenDate(words, at, zone) {
|
|
5
|
-
const absolute = String(words).match(/20\d{2}-\d{2}-\d{2}(?: \d{2}:\d{2})?/);
|
|
6
|
-
if (absolute)
|
|
7
|
-
return {
|
|
8
|
-
dueAt: zonedTime(absolute[0], zone),
|
|
9
|
-
duePrecision: absolute[0].includes(":") ? "time" : "day",
|
|
10
|
-
};
|
|
11
|
-
if (/后天|明天|明晚|今晚|今天/.test(words)) {
|
|
12
|
-
const offset = /后天/.test(words) ? 2 : /明天|明晚/.test(words) ? 1 : 0;
|
|
13
|
-
return {
|
|
14
|
-
dueAt: zonedTime(dayKey(at + offset * 86400000, zone) + " 23:59", zone),
|
|
15
|
-
duePrecision: "day",
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
return { dueAt: null, duePrecision: "none" };
|
|
19
|
-
}
|
|
20
|
-
export function nextAwakeSlot(mind, now) {
|
|
21
|
-
const zone = mind.timeZone(),
|
|
22
|
-
rhythm = mind.nature.current(now).rhythm;
|
|
23
|
-
let at = Math.ceil(now / 300000) * 300000;
|
|
24
|
-
if (rhythm?.enabled) {
|
|
25
|
-
const clock = localClock(now, zone),
|
|
26
|
-
wake = zonedTime(dayKey(now, zone) + " " + rhythm.wake, zone);
|
|
27
|
-
if (wake > now) at = wake;
|
|
28
|
-
else if (
|
|
29
|
-
mind.affect.state(now).phase === "asleep" ||
|
|
30
|
-
mind.affect.state(now).phase === "sleepy"
|
|
31
|
-
)
|
|
32
|
-
at = zonedTime(dayKey(now + 86400000, zone) + " " + rhythm.wake, zone);
|
|
33
|
-
}
|
|
34
|
-
return at;
|
|
35
|
-
}
|
|
36
|
-
export function deadlineAt(at, precision, zone) {
|
|
37
|
-
if (!Number.isFinite(at)) return null;
|
|
38
|
-
return precision === "day"
|
|
39
|
-
? zonedTime(dayKey(at, zone) + " 23:59", zone) + 59999
|
|
40
|
-
: at;
|
|
41
|
-
}
|
|
42
|
-
export function taskSchedule(task, time, now = time.now()) {
|
|
43
|
-
const plan = task.checkpoint?.schedule;
|
|
44
|
-
const waiting = task.state === "waiting";
|
|
45
|
-
const focus =
|
|
46
|
-
plan?.durationMinutes || plan?.focusMinutes || time.settings().focusMinutes;
|
|
47
|
-
const proposed =
|
|
48
|
-
plan?.chosenAt || plan?.proposedAt > now
|
|
49
|
-
? plan.proposedAt
|
|
50
|
-
: task.state === "scheduled" && task.ready_at > now
|
|
51
|
-
? task.ready_at
|
|
52
|
-
: null;
|
|
53
|
-
const live =
|
|
54
|
-
task.state === "doing"
|
|
55
|
-
? time.db
|
|
56
|
-
.prepare(
|
|
57
|
-
"SELECT started FROM mind_time_spans WHERE task_id=? AND ended IS NULL",
|
|
58
|
-
)
|
|
59
|
-
.get(task.id)
|
|
60
|
-
: null;
|
|
61
|
-
return {
|
|
62
|
-
earliestAt: task.ready_at,
|
|
63
|
-
proposedAt: proposed,
|
|
64
|
-
proposedEnd: proposed ? proposed + focus * 60000 : null,
|
|
65
|
-
startedAt: live?.started || null,
|
|
66
|
-
focusMinutes: focus,
|
|
67
|
-
chosenAt: plan?.chosenAt || null,
|
|
68
|
-
chosenBy: plan?.chosenBy || null,
|
|
69
|
-
durationMinutes: plan?.durationMinutes || null,
|
|
70
|
-
reason: plan?.reason || "",
|
|
71
|
-
conditional: waiting,
|
|
72
|
-
deadlinePrecision: task.due_precision || "time",
|
|
73
|
-
deadlineAt: task.due_at,
|
|
74
|
-
nextAttemptAt:
|
|
75
|
-
task.next_step > now && task.next_step < 8e15 ? task.next_step : null,
|
|
76
|
-
outcome:
|
|
77
|
-
task.activity === "game"
|
|
78
|
-
? gameText(plan?.outcome || task.checkpoint?.contract?.outcome || "")
|
|
79
|
-
: plan?.outcome || task.checkpoint?.contract?.outcome || "",
|
|
80
|
-
estimatedMs: task.checkpoint?.activityClock?.plannedMs || null,
|
|
81
|
-
condition: waiting ? task.wait_reason : "",
|
|
82
|
-
};
|
|
83
|
-
}
|
|
1
|
+
import { dayKey, zonedTime } from "../util.js";
|
|
2
|
+
import { localClock } from "../../core/conversation-cues.js";
|
|
3
|
+
import { gameText } from "./presentation.js";
|
|
4
|
+
export function spokenDate(words, at, zone) {
|
|
5
|
+
const absolute = String(words).match(/20\d{2}-\d{2}-\d{2}(?: \d{2}:\d{2})?/);
|
|
6
|
+
if (absolute)
|
|
7
|
+
return {
|
|
8
|
+
dueAt: zonedTime(absolute[0], zone),
|
|
9
|
+
duePrecision: absolute[0].includes(":") ? "time" : "day",
|
|
10
|
+
};
|
|
11
|
+
if (/后天|明天|明晚|今晚|今天/.test(words)) {
|
|
12
|
+
const offset = /后天/.test(words) ? 2 : /明天|明晚/.test(words) ? 1 : 0;
|
|
13
|
+
return {
|
|
14
|
+
dueAt: zonedTime(dayKey(at + offset * 86400000, zone) + " 23:59", zone),
|
|
15
|
+
duePrecision: "day",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return { dueAt: null, duePrecision: "none" };
|
|
19
|
+
}
|
|
20
|
+
export function nextAwakeSlot(mind, now) {
|
|
21
|
+
const zone = mind.timeZone(),
|
|
22
|
+
rhythm = mind.nature.current(now).rhythm;
|
|
23
|
+
let at = Math.ceil(now / 300000) * 300000;
|
|
24
|
+
if (rhythm?.enabled) {
|
|
25
|
+
const clock = localClock(now, zone),
|
|
26
|
+
wake = zonedTime(dayKey(now, zone) + " " + rhythm.wake, zone);
|
|
27
|
+
if (wake > now) at = wake;
|
|
28
|
+
else if (
|
|
29
|
+
mind.affect.state(now).phase === "asleep" ||
|
|
30
|
+
mind.affect.state(now).phase === "sleepy"
|
|
31
|
+
)
|
|
32
|
+
at = zonedTime(dayKey(now + 86400000, zone) + " " + rhythm.wake, zone);
|
|
33
|
+
}
|
|
34
|
+
return at;
|
|
35
|
+
}
|
|
36
|
+
export function deadlineAt(at, precision, zone) {
|
|
37
|
+
if (!Number.isFinite(at)) return null;
|
|
38
|
+
return precision === "day"
|
|
39
|
+
? zonedTime(dayKey(at, zone) + " 23:59", zone) + 59999
|
|
40
|
+
: at;
|
|
41
|
+
}
|
|
42
|
+
export function taskSchedule(task, time, now = time.now()) {
|
|
43
|
+
const plan = task.checkpoint?.schedule;
|
|
44
|
+
const waiting = task.state === "waiting";
|
|
45
|
+
const focus =
|
|
46
|
+
plan?.durationMinutes || plan?.focusMinutes || time.settings().focusMinutes;
|
|
47
|
+
const proposed =
|
|
48
|
+
plan?.chosenAt || plan?.proposedAt > now
|
|
49
|
+
? plan.proposedAt
|
|
50
|
+
: task.state === "scheduled" && task.ready_at > now
|
|
51
|
+
? task.ready_at
|
|
52
|
+
: null;
|
|
53
|
+
const live =
|
|
54
|
+
task.state === "doing"
|
|
55
|
+
? time.db
|
|
56
|
+
.prepare(
|
|
57
|
+
"SELECT started FROM mind_time_spans WHERE task_id=? AND ended IS NULL",
|
|
58
|
+
)
|
|
59
|
+
.get(task.id)
|
|
60
|
+
: null;
|
|
61
|
+
return {
|
|
62
|
+
earliestAt: task.ready_at,
|
|
63
|
+
proposedAt: proposed,
|
|
64
|
+
proposedEnd: proposed ? proposed + focus * 60000 : null,
|
|
65
|
+
startedAt: live?.started || null,
|
|
66
|
+
focusMinutes: focus,
|
|
67
|
+
chosenAt: plan?.chosenAt || null,
|
|
68
|
+
chosenBy: plan?.chosenBy || null,
|
|
69
|
+
durationMinutes: plan?.durationMinutes || null,
|
|
70
|
+
reason: plan?.reason || "",
|
|
71
|
+
conditional: waiting,
|
|
72
|
+
deadlinePrecision: task.due_precision || "time",
|
|
73
|
+
deadlineAt: task.due_at,
|
|
74
|
+
nextAttemptAt:
|
|
75
|
+
task.next_step > now && task.next_step < 8e15 ? task.next_step : null,
|
|
76
|
+
outcome:
|
|
77
|
+
task.activity === "game"
|
|
78
|
+
? gameText(plan?.outcome || task.checkpoint?.contract?.outcome || "")
|
|
79
|
+
: plan?.outcome || task.checkpoint?.contract?.outcome || "",
|
|
80
|
+
estimatedMs: task.checkpoint?.activityClock?.plannedMs || null,
|
|
81
|
+
condition: waiting ? task.wait_reason : "",
|
|
82
|
+
};
|
|
83
|
+
}
|