@musnows/scriverse 0.4.7 → 0.4.10
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/dist/ai.js +33 -15
- package/dist/ai.js.map +1 -1
- package/dist/app.js +117 -24
- package/dist/app.js.map +1 -1
- package/dist/collaboration-presence.js +77 -2
- package/dist/collaboration-presence.js.map +1 -1
- package/dist/database.js +30 -1
- package/dist/database.js.map +1 -1
- package/dist/pagination.js +3 -2
- package/dist/pagination.js.map +1 -1
- package/dist/public/app.js +513 -43
- package/dist/public/avatar-crop.d.ts +57 -0
- package/dist/public/avatar-crop.js +201 -0
- package/dist/public/display-labels.d.ts +1 -0
- package/dist/public/display-labels.js +8 -0
- package/dist/public/index.html +68 -34
- package/dist/public/markdown.js +16 -0
- package/dist/public/styles.css +110 -13
- package/dist/store.js +8 -5
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +12 -2
- package/dist/user-auth.js.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
package/dist/ai.js
CHANGED
|
@@ -32,7 +32,7 @@ function thinkingParameters(provider, model) {
|
|
|
32
32
|
return {};
|
|
33
33
|
return { thinking: { type: boolValue(model, "thinking_enabled") ? "enabled" : "disabled" } };
|
|
34
34
|
}
|
|
35
|
-
const AGENT_TOOL_IDS = ["story_index", "read_chapters", "grep", "
|
|
35
|
+
const AGENT_TOOL_IDS = ["story_index", "read_chapters", "grep", "search_story_entities", "read_character_sections"];
|
|
36
36
|
const MAX_AGENT_TOOL_ROUNDS = 6;
|
|
37
37
|
const MAX_AGENT_TOOL_CALLS = 12;
|
|
38
38
|
const MAX_CONFIGURED_AGENT_TOOL_CALLS = 48;
|
|
@@ -48,7 +48,7 @@ const grepArguments = z.object({
|
|
|
48
48
|
keyword: z.string().trim().min(1).max(200),
|
|
49
49
|
limit: z.number().int().min(1).max(100).default(20)
|
|
50
50
|
}).strict();
|
|
51
|
-
const
|
|
51
|
+
const searchStoryEntitiesArguments = z.object({
|
|
52
52
|
query: z.string().trim().min(1).max(200),
|
|
53
53
|
categories: z.array(z.enum(["setting", "character", "race", "organization", "timeline", "relationship", "outline", "foreshadow"])).max(8).default([])
|
|
54
54
|
}).strict();
|
|
@@ -81,11 +81,11 @@ const AGENT_TOOL_DEFINITIONS = {
|
|
|
81
81
|
parameters: { type: "object", properties: { keyword: { type: "string", minLength: 1, maxLength: 200 }, limit: { type: "integer", minimum: 1, maximum: 100, default: 20 } }, required: ["keyword"], additionalProperties: false }
|
|
82
82
|
}
|
|
83
83
|
},
|
|
84
|
-
|
|
84
|
+
search_story_entities: {
|
|
85
85
|
type: "function",
|
|
86
86
|
function: {
|
|
87
|
-
name: "
|
|
88
|
-
description: "
|
|
87
|
+
name: "search_story_entities",
|
|
88
|
+
description: "按短关键词在结构化作品实体中做字面/子串匹配:设定、人物(含 Markdown 档案章节)、种族、组织、时间线、关系、大纲和伏笔。不是语义检索或知识库问答;请传入实体名、别名、标题或其子串,不要传入自然语言整句。返回简要匹配项;人物结果含 sectionId 时可再调用 read_character_sections 精读。无匹配时改用更短关键词,或改用 story_index / grep。",
|
|
89
89
|
parameters: { type: "object", properties: { query: { type: "string", minLength: 1, maxLength: 200 }, categories: { type: "array", items: { type: "string", enum: ["setting", "character", "race", "organization", "timeline", "relationship", "outline", "foreshadow"] }, maxItems: 8 } }, required: ["query"], additionalProperties: false }
|
|
90
90
|
}
|
|
91
91
|
},
|
|
@@ -93,7 +93,7 @@ const AGENT_TOOL_DEFINITIONS = {
|
|
|
93
93
|
type: "function",
|
|
94
94
|
function: {
|
|
95
95
|
name: "read_character_sections",
|
|
96
|
-
description: "读取指定人物 Markdown 档案章节的摘要或原文。先通过
|
|
96
|
+
description: "读取指定人物 Markdown 档案章节的摘要或原文。先通过 search_story_entities 获取 sectionId;每次最多读取 3 个章节。",
|
|
97
97
|
parameters: { type: "object", properties: { sectionIds: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 3 }, include: { type: "string", enum: ["summary", "content", "both"] } }, required: ["sectionIds"], additionalProperties: false }
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -1459,7 +1459,7 @@ export class AiManager {
|
|
|
1459
1459
|
? [
|
|
1460
1460
|
`当前可用作品查询工具:${enabledToolIds.join("、")}。`,
|
|
1461
1461
|
"当作者询问当前作品、项目、章节、情节、人物、关系、世界观或设定,而预加载上下文为空或不足时,必须先调用工具主动查询;不得直接声称没有上下文,也不得先要求作者补充本系统已经能够查询的信息。",
|
|
1462
|
-
"整体介绍、作品基本信息、目录或章节定位优先调用 story_index;按关键字定位正文段落时调用 grep;已知章节 ID 且需要原文事实或精确措辞时调用 read_chapters
|
|
1462
|
+
"整体介绍、作品基本信息、目录或章节定位优先调用 story_index;按关键字定位正文段落时调用 grep;已知章节 ID 且需要原文事实或精确措辞时调用 read_chapters;查找设定、人物、组织、时间线、关系、大纲或伏笔时调用 search_story_entities(传入短实体名或关键词子串,勿用自然语言整句);人物匹配结果包含 sectionId 且需要背景故事、能力或经历原文时调用 read_character_sections。",
|
|
1463
1463
|
"根据问题选择最少且必要的工具。工具结果仍不足时才说明未知,并明确已经查询过什么;不要重复无效调用。"
|
|
1464
1464
|
].join("\n")
|
|
1465
1465
|
: "";
|
|
@@ -1543,7 +1543,7 @@ export class AiManager {
|
|
|
1543
1543
|
const schema = name === "story_index" ? storyIndexArguments
|
|
1544
1544
|
: name === "read_chapters" ? readChaptersArguments
|
|
1545
1545
|
: name === "grep" ? grepArguments
|
|
1546
|
-
: name === "
|
|
1546
|
+
: name === "search_story_entities" ? searchStoryEntitiesArguments
|
|
1547
1547
|
: name === "read_character_sections" ? readCharacterSectionsArguments
|
|
1548
1548
|
: null;
|
|
1549
1549
|
if (!schema) {
|
|
@@ -1636,7 +1636,7 @@ export class AiManager {
|
|
|
1636
1636
|
result: { ok: true, data: { keyword, limit, matches } }
|
|
1637
1637
|
};
|
|
1638
1638
|
}
|
|
1639
|
-
if (name === "
|
|
1639
|
+
if (name === "search_story_entities") {
|
|
1640
1640
|
const { query, categories: categoryList } = args;
|
|
1641
1641
|
const categories = new Set(categoryList);
|
|
1642
1642
|
const allowed = new Set(["setting", "character", "race", "organization", "timeline", "relationship", "outline", "foreshadow"]);
|
|
@@ -1647,7 +1647,25 @@ export class AiManager {
|
|
|
1647
1647
|
...this.store.listChapterOutlines(workId).map((item) => ({ type: "outline", id: item.chapterId, title: item.chapterTitle, snippet: `${item.goal} ${item.conflict} ${item.turningPoint} ${item.notes}` })),
|
|
1648
1648
|
...this.store.listForeshadows(workId).map((item) => ({ type: "foreshadow", id: item.id, title: item.title, snippet: `${item.description} ${item.resolutionNote}` }))
|
|
1649
1649
|
].filter((item) => allowed.has(item.type) && (!categories.size || categories.has(item.type)) && `${item.title} ${item.snippet}`.toLocaleLowerCase("zh-CN").includes(query.toLocaleLowerCase("zh-CN"))).slice(0, 20);
|
|
1650
|
-
|
|
1650
|
+
const combined = [...matches, ...extra].slice(0, 30);
|
|
1651
|
+
return {
|
|
1652
|
+
id: toolCall.id,
|
|
1653
|
+
name,
|
|
1654
|
+
calledAt,
|
|
1655
|
+
arguments: { query, categories: categoryList },
|
|
1656
|
+
status: "completed",
|
|
1657
|
+
result: {
|
|
1658
|
+
ok: true,
|
|
1659
|
+
data: {
|
|
1660
|
+
query,
|
|
1661
|
+
matchMode: "literal_substring",
|
|
1662
|
+
matches: combined,
|
|
1663
|
+
...(combined.length === 0
|
|
1664
|
+
? { hint: "无字面匹配。请改用更短的实体名、别名或标题子串;也可改用 story_index 浏览目录,或用 grep 搜索正文关键字。" }
|
|
1665
|
+
: {})
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1651
1669
|
}
|
|
1652
1670
|
if (name === "read_character_sections") {
|
|
1653
1671
|
const { sectionIds, include } = args;
|
|
@@ -2426,10 +2444,10 @@ export class AiManager {
|
|
|
2426
2444
|
const characters = this.store.listCharacters(workId);
|
|
2427
2445
|
if (characters.length < 2)
|
|
2428
2446
|
return { characterCount: characters.length, candidateCount: 0, reviewIds: [], skipped: [] };
|
|
2429
|
-
const requiredTools = ["
|
|
2447
|
+
const requiredTools = ["search_story_entities", "grep", "read_chapters"];
|
|
2430
2448
|
const enabledTools = new Set(this.enabledAgentToolIds(workId, "book-analysis", requiredTools));
|
|
2431
|
-
if (!enabledTools.has("
|
|
2432
|
-
throw new AppError(409, "AI_TOOLS_REQUIRED", "
|
|
2449
|
+
if (!enabledTools.has("search_story_entities") || !enabledTools.has("grep")) {
|
|
2450
|
+
throw new AppError(409, "AI_TOOLS_REQUIRED", "角色查重需要启用“搜索作品实体”和“正文搜索”工具");
|
|
2433
2451
|
}
|
|
2434
2452
|
const roster = characters.map((character) => {
|
|
2435
2453
|
const attributes = character.attributes;
|
|
@@ -2459,7 +2477,7 @@ export class AiManager {
|
|
|
2459
2477
|
"审核角色规范表,找出可能把同一个角色误建成两个档案的组合,最多输出 12 组。",
|
|
2460
2478
|
"角色规范表:",
|
|
2461
2479
|
roster,
|
|
2462
|
-
"你必须主动使用
|
|
2480
|
+
"你必须主动使用 search_story_entities 按角色主名或别名查找角色档案和关系,并使用 grep 分别搜索疑似组合两侧的主名或别名;需要上下文时再用 read_chapters。工具调用总数不得超过 48 次。",
|
|
2463
2481
|
"不能仅凭名字相似判断同一人。角色彼此对话、互相提及、同时出现、身份或种族冲突,都是不同角色的强反证。",
|
|
2464
2482
|
"只把有原文连续引文支持的 same 或 uncertain 组合放入结果;确认是不同角色的组合无需输出。",
|
|
2465
2483
|
"输出 JSON 数组。每项字段:leftCharacterId、rightCharacterId、verdict(same/uncertain)、confidence(0到1)、reason、evidence(数组,每项含 chapterId、quote、supports)、contradictions(字符串数组)。",
|
|
@@ -2468,7 +2486,7 @@ export class AiManager {
|
|
|
2468
2486
|
extraSystemPrompt: "你是谨慎的角色身份消歧审核器。任何结论都只是待作者确认的建议,禁止自动合并角色。"
|
|
2469
2487
|
});
|
|
2470
2488
|
const toolNames = new Set(generated.toolCalls.filter((call) => call.status === "completed").map((call) => call.name));
|
|
2471
|
-
if (!toolNames.has("
|
|
2489
|
+
if (!toolNames.has("search_story_entities") || !toolNames.has("grep")) {
|
|
2472
2490
|
throw new AppError(502, "CHARACTER_AUDIT_INCOMPLETE", "AI 未完成角色资料查询和正文搜索,本次查重结果未保存");
|
|
2473
2491
|
}
|
|
2474
2492
|
const extracted = extractJson(generated.content);
|