Skip to content

Commit 8f006aa

Browse files
committed
merge: promote beta updates to main
2 parents 265d9a2 + 4ba8289 commit 8f006aa

225 files changed

Lines changed: 15895 additions & 2791 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎AGENTS.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@
2020
- deterministic post-processing of already-structured AI output.
2121
- When adding a new capability, first extend the AI schema / structured output / tool contract. Do not patch behavior by stacking special-case string rules.
2222

23+
## Auto-Director Quality Gate Rules (Highest Priority)
24+
25+
- Chapter audit, acceptance, and quality-loop results must not automatically block the global auto-director or full-book execution chain.
26+
- Non-global chapter quality problems, including `local_patch_plan`, `continue_with_warning`, `patchable_obligation_gap`, `draft_obligation_unmet`, recoverable repair failures, and `defer_and_continue` quality debt, must be recorded as chapter-level quality debt or local repair guidance and allow the remaining chapter range to continue.
27+
- Only an explicit `stop_for_replan`, `replan_required`, `recommendedAction=replan`, unrecoverable generation failure with no usable chapter content, or a runtime safety/data integrity failure may stop the global chain.
28+
- Do not route local audit issues into `replanAlertDetails`, `PIPELINE_REPLAN_REQUIRED`, or the `replan_required` checkpoint unless the structured AI/runtime decision explicitly says the neighboring chapter plan must stop for replan.
29+
- If local repair has already been attempted and residual issues remain but the chapter has usable content, prefer degraded finalization plus quality debt over failing the whole auto-director task.
30+
- UI, task projection, and recovery logic must preserve this distinction: local quality debt is a visible warning and follow-up item, not a failed auto-director workflow.
31+
- Book-level auto-director projections with `failed`, `blocked`, or `waiting_recovery` status and a latest task must remain visible in AI cockpit, task drawer, and recovery entrypoints even when the URL does not include `directorTaskId`; `workspaceTaskId` is a manual workspace lane and must never be used as a substitute director task id.
32+
2333
## Product Context (Highest Priority)
2434

2535
- The primary target users of this project are complete writing beginners who do not understand fiction craft, structure, or novel production workflows.

‎README.md‎

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -124,24 +124,54 @@
124124

125125
完整历史更新见 [docs/releases/release-notes.md](./docs/releases/release-notes.md)。
126126

127-
### 2026-05-29
128-
129-
重大更新:本次把 2026-05-24 之后在预发布分支验证过的自动导演、章节生产、角色阵容、模型选择、灵感辅助和运行态治理合并进主线,并同步准备 Windows 桌面版 `0.3.16`。
130-
131-
- 自动导演从“当前项目继续”更顺:弹窗会先展示卷规划、拆章同步、章节细化、正文书写和质量进度,并按系统推荐位置一键继续;需要连续推进时,可以直接选择“推进至第 N 章”。
132-
- 已有项目从故事宏观规划、角色准备、卷战略或章节执行接管时,会更准确识别当前真实进度;已经完成的角色准备不会被重新显示为待推进,章节范围也不会误套到整书前置接管上。
133-
- 角色阵容应用更快:手动应用阵容会先同步角色和关系,外显资料与角色动态在后台补齐,按钮不再长时间卡在“应用中”。
134-
- 节奏拆章和章节执行衔接更直接:拆好的章节会自动连接到执行章节,旧项目也会优先按章节身份匹配,减少章节执行区看不到拆章结果的情况。
135-
- 章节生产链更稳:质量债会绑定到实际触发问题且已有正文的章节,空章节不会被误跳过;时间线、状态抽取和质量修复会更稳定地支撑后续章节继续写。
136-
- 自动导演驾驶舱状态统一:进度弹窗、小说页提示、任务中心和执行详情会优先使用同一份驾驶舱状态,减少同一任务在不同入口显示不同进度、不同等待状态或无效确认按钮。
137-
- 运行中的任务会优先显示真实推进态;历史审批投影、章节标题提醒或旧的等待确认信息不会再把正在细化、写作或审校的任务误显示成“等待确认”。
138-
- “让 AI 从当前项目继续自动导演”只会把真正的自动导演任务识别为当前任务;普通编辑流程任务不会再让接管弹窗误显示“进入当前任务”。
139-
- 未完成的等待确认提醒会明确显示为“收起此提醒”,避免让用户误以为收起动作会完成或结束任务。
140-
- 自动导演创建更适合新手默认路径:默认推荐“先准备到可开写”,并新增“读者频道倾向”,帮助 AI 判断男频 / 女频等阅读预期和爽点侧重。
141-
- 起始想法旁新增“没有想法?”入口,可临时生成 5 个横向灵感卡片;点击“使用这个”会把灵感填入上方输入框,已有内容时会先确认再覆盖。
142-
- 模型选择器切换厂商时会即时刷新该厂商模型列表,减少先去设置页刷新再回来选择的操作。
143-
- 首页、小说列表和任务恢复等首屏读取更轻,模型状态、恢复摘要和小说列表会分级加载,打开页面时更不容易被后台接口拖慢。
144-
- 后端章节运行时、自动导演模块、小说应用服务、事件副作用和路由边界继续收敛,Prompt 调用也补上质量遥测,为后续排查长链路生成和恢复问题提供更稳定的基础。
127+
### 2026-06-05
128+
129+
自动导演连续执行更稳,章节生成上下文更聚焦:系统会减少无明确目标窗口的伏笔逾期误判,避免批量写章被重复账本错误打断,并让章节检索更贴合当前章节任务。
130+
131+
- 节奏拆章中的章节列表聚焦查看、生成和删改既有章节,不再提供容易绕开自动规划链路的手动新增章节入口。
132+
- 伏笔账本同步会识别同义标题并复用已有未完成账本,减少 AI 把同一 payoff 新建成重复逾期项。
133+
- 缺少明确目标章节窗口的逾期 payoff 会降级为待跟进风险,不再直接触发整窗重规划或停止后续章节执行。
134+
- 章节写作会用当前章节目标、必须推进事项、冲突和出场角色组装知识库检索问题,让 RAG 上下文更贴近本章写作任务。
135+
- 章节生成上下文会减少未被正文写作实际消费的旧式大块背景拼接,把相关信息放入结构化运行时上下文,降低无效上下文膨胀。
136+
137+
### 2026-06-04
138+
139+
自动导演连续写章更省资源、更少重复等待:系统会复用同一章节同一正文的质量门控结果,并收敛时间线上下文,减少任务重启后重复审校、重复抽取和重复细化章节合同。
140+
141+
- 章节质量门控会复用已成功的接收判断和时间线检测,同一正文在任务取消、失败或重启后不再默认重新消耗一次 AI 调用。
142+
- 时间线抽取只携带近期关键事件和必要钩子,让后续章节不会因为整本历史持续增长而越来越慢。
143+
- 章节接收判断会区分硬阻断和可继续跟进的质量债务,轻量义务风险会保留提示并继续推进,减少不必要的自动补写。
144+
- 已经准备好任务单和场景预算的章节不会在恢复流程里重复生成章节执行合同,带有新指令的重生成仍可正常覆盖。
145+
- 自动导演会监控每章 AI 用量,单章消耗异常时自动暂停后续执行,避免上下文膨胀或质量循环继续扩大成本。
146+
- 时间线抽取不再阻塞章节接收,正文通过接收后会继续完成时间线定稿;下一章开始前仍会自动补齐必要的时间线 checkpoint。
147+
- 自动导演的用量暂停会聚焦仍在推进的章节范围,已经完成的章节不会因为历史累计用量偏高而反复拦住后续章节。
148+
- 章节批次会按当前执行范围统一判断审校、修复和状态提交;已经登记为可继续质量债的局部问题不会再把自动导演卡在章节状态提交,减少反复恢复和异常 Token 消耗。
149+
- 时间线定稿和章节资产回灌会在调用 AI 前先抢占同步记录,减少同一章节同一正文被并发后台入口重复抽取的情况。
150+
151+
### 2026-06-03
152+
153+
章节批量生成更不容易被重复质量债务打断:系统会区分“继续但提示”“局部修复计划”和“必须停止重规划”,避免同一批逾期伏笔在连续章节里反复触发整窗重规划。
154+
155+
- 短窗口、未直接影响当前章的逾期伏笔会作为提示保留,不再默认停止后续章节流水线。
156+
- 只有明确需要整窗调整的重规划建议才会暂停后续章节;局部计划问题会进入修复和质量提示,不再直接升级成整本流程阻塞。
157+
- 章节任务里的“必须推进”会过滤接收闸门不可用、结构化缺口等系统审计标签,避免 AI 把系统问题当成剧情义务去写。
158+
- 章节质量闭环会按新的重规划动作判断风险,减少把可继续的质量债务显示成必须重规划。
159+
160+
### 2026-06-02
161+
162+
重大更新:世界观从“字段表单”升级为更适合新手理解和直接开书的世界手册与世界骨架流程,同时自动导演可以在宏观规划后自动准备本书世界,让角色、地点、势力和章节上下文更容易保持一致。
163+
164+
- 世界生成默认走“世界意图 -> 世界规模 -> 骨架预览 -> 保存世界”的流程,用户可以选择轻量舞台、标准长篇或复杂群像,并调整规则、势力、地点、冲突和故事入口数量。
165+
- 世界库和世界工作台会以世界卡片、世界手册、核心规则、主要势力、关键地点、关系网络和完整度诊断来展示结果,减少直接面对 `background / geography / factions` 这类字段名的负担。
166+
- 世界地图和势力图谱拥有更完整的数据支撑:地点包含相对坐标、方位、风险、控制势力和连接关系,势力包含目标、资源、控制地点、关系类型和紧张度。
167+
- 小说内新增本书世界管理能力:可以从世界库导入为本书副本,也可以根据本书主题生成专属世界,并由用户决定是否保存回世界库或手动同步差异。
168+
- 自动导演在宏观规划和书级约定之后新增本书世界准备步骤;没有选择参考世界时,系统会默认生成本书世界并在角色准备前构建可用的世界上下文。
169+
- 角色生成可以选择是否基于当前世界观,并能结合势力倾向、世界规则和身份边界生成更贴合本书舞台的人物。
170+
- 世界生成的参考资料检索更收敛,默认只使用当前世界、模板和用户明确选择的参考内容,减少无关知识库文档混入新世界生成。
171+
- 接收闸门会更稳定地识别“本章必须完成但正文缺失”的义务,减少把缺失义务输出成散乱文本而触发修复器的情况。
172+
- 时间线抽取会更稳定地记录事件、状态变化和后续钩子,中文类型名或简写钩子会被归一到可保存的结构化格式。
173+
- 章节资产抽取会更稳地处理资源状态,自动导演的高创造温度不会再传染到事实抽取类任务。
174+
- JSON 修复日志可以按 Prompt 和失败字段聚合,方便定位到底是示例、枚举、上下文还是模型输出导致的结构化失败。
145175

146176
## 功能预览
147177
### 功能概览中的95%以上编写都是AI完成

‎client/src/api/bookAnalysis.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export async function createBookAnalysis(payload: {
3434
temperature?: number;
3535
maxTokens?: number;
3636
includeTimeline?: boolean;
37+
enabledSectionKeys?: BookAnalysisSectionKey[];
3738
}) {
3839
const { data } = await apiClient.post<ApiResponse<BookAnalysisDetail>>("/book-analysis", payload);
3940
return data;

‎client/src/api/novel/characters.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ export async function generateCharacterCastOptions(
122122
model?: string;
123123
temperature?: number;
124124
storyInput?: string;
125+
useWorldContext?: boolean;
126+
worldFocusHints?: {
127+
preferFaction?: string;
128+
forceCompliance?: boolean;
129+
};
125130
},
126131
) {
127132
const { data } = await apiClient.post<ApiResponse<CharacterCastOption[]>>(

‎client/src/api/novelWorldSlice.ts‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,56 @@
11
import type { ApiResponse } from "@ai-novel/shared/types/api";
22
import type { LLMProvider } from "@ai-novel/shared/types/llm";
3+
import type {
4+
NovelWorldGenerateInput,
5+
NovelWorldImportInput,
6+
NovelWorldManualInput,
7+
NovelWorldSaveToLibraryInput,
8+
NovelWorldSyncDiff,
9+
NovelWorldSyncInput,
10+
NovelWorldView,
11+
} from "@ai-novel/shared/types/novelWorld";
312
import type {
413
StoryWorldSliceBuilderMode,
514
StoryWorldSliceOverrides,
615
StoryWorldSliceView,
716
} from "@ai-novel/shared/types/storyWorldSlice";
817
import { apiClient } from "./client";
918

19+
export async function getNovelWorld(id: string) {
20+
const { data } = await apiClient.get<ApiResponse<NovelWorldView>>(`/novels/${id}/novel-world`);
21+
return data;
22+
}
23+
24+
export async function importNovelWorldFromLibrary(id: string, payload: NovelWorldImportInput) {
25+
const { data } = await apiClient.post<ApiResponse<NovelWorldView>>(`/novels/${id}/novel-world/import`, payload);
26+
return data;
27+
}
28+
29+
export async function createManualNovelWorld(id: string, payload: NovelWorldManualInput) {
30+
const { data } = await apiClient.post<ApiResponse<NovelWorldView>>(`/novels/${id}/novel-world/manual`, payload);
31+
return data;
32+
}
33+
34+
export async function generateNovelWorldFromTheme(id: string, payload: NovelWorldGenerateInput) {
35+
const { data } = await apiClient.post<ApiResponse<NovelWorldView>>(`/novels/${id}/novel-world/generate`, payload);
36+
return data;
37+
}
38+
39+
export async function saveNovelWorldToLibrary(id: string, payload: NovelWorldSaveToLibraryInput) {
40+
const { data } = await apiClient.post<ApiResponse<NovelWorldView>>(`/novels/${id}/novel-world/save-to-library`, payload);
41+
return data;
42+
}
43+
44+
export async function getNovelWorldSyncDiff(id: string) {
45+
const { data } = await apiClient.get<ApiResponse<NovelWorldSyncDiff>>(`/novels/${id}/novel-world/sync-diff`);
46+
return data;
47+
}
48+
49+
export async function syncNovelWorldWithLibrary(id: string, payload: NovelWorldSyncInput) {
50+
const { data } = await apiClient.post<ApiResponse<NovelWorldSyncDiff>>(`/novels/${id}/novel-world/sync`, payload);
51+
return data;
52+
}
53+
1054
export async function getNovelWorldSlice(id: string) {
1155
const { data } = await apiClient.get<ApiResponse<StoryWorldSliceView>>(`/novels/${id}/world-slice`);
1256
return data;

‎client/src/api/queryKeys.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export const queryKeys = {
3232
autoDirectorTask: (id: string) => ["novels", "auto-director-task", id] as const,
3333
directorBookAutomation: (id: string) => ["novels", "director-book-automation", id] as const,
3434
autoDirectorTakeoverReadiness: (id: string) => ["novels", "auto-director-takeover-readiness", id] as const,
35+
novelWorld: (id: string) => ["novels", "novel-world", id] as const,
36+
novelWorldSyncDiff: (id: string) => ["novels", "novel-world", id, "sync-diff"] as const,
3537
worldSlice: (id: string) => ["novels", "world-slice", id] as const,
3638
snapshots: (id: string) => ["novels", "snapshots", id] as const,
3739
},

‎client/src/api/world.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ import type {
1717
WorldOptionRefinementLevel,
1818
WorldPropertyOption,
1919
WorldReferenceAnchor,
20+
WorldGenerationBlueprint,
2021
WorldReferenceMode,
22+
WorldReferenceContext,
2123
WorldReferenceSeedBundle,
24+
WorldSkeletonGenerationOptions,
25+
WorldSkeletonGenerationPayload,
2226
} from "@ai-novel/shared/types/worldWizard";
2327
import { apiClient } from "./client";
2428

@@ -215,6 +219,23 @@ export async function analyzeWorldInspiration(payload: {
215219
return data;
216220
}
217221

222+
export async function generateWorldSkeleton(payload: {
223+
idea: string;
224+
worldType?: string;
225+
template?: string;
226+
referenceContext?: WorldReferenceContext | null;
227+
blueprint?: WorldGenerationBlueprint | null;
228+
options: WorldSkeletonGenerationOptions;
229+
provider?: LLMProvider;
230+
model?: string;
231+
}) {
232+
const { data } = await apiClient.post<ApiResponse<WorldSkeletonGenerationPayload>>(
233+
"/worlds/skeleton/generate",
234+
payload,
235+
);
236+
return data;
237+
}
238+
218239
export async function suggestWorldAxioms(
219240
id: string,
220241
payload?: {

‎client/src/components/layout/NovelWorkspaceRail.tsx‎

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
ListTodo,
1010
} from "lucide-react";
1111
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
12-
import type { DirectorContinuationMode, DirectorLockScope } from "@ai-novel/shared/types/novelDirector";
12+
import type { DirectorLockScope } from "@ai-novel/shared/types/novelDirector";
1313
import type { VolumePlan } from "@ai-novel/shared/types/novel";
1414
import type { UnifiedTaskDetail } from "@ai-novel/shared/types/task";
1515
import { getNovelDetail, getNovelQualityReport, getNovelVolumeWorkspace } from "@/api/novel";
@@ -29,7 +29,7 @@ import DirectorBookAutomationCard from "@/components/autoDirector/DirectorBookAu
2929
import NovelAutoDirectorProgressPanel from "@/pages/novels/components/NovelAutoDirectorProgressPanel";
3030
import { shouldShowPinnedBookAutomationProjection } from "@/pages/novels/novelEditAutomationStatus";
3131
import { cn } from "@/lib/utils";
32-
import { resolveWorkflowContinuationFeedback } from "@/lib/novelWorkflowContinuation";
32+
import { resolveDirectorContinueMode, resolveWorkflowContinuationFeedback } from "@/lib/novelWorkflowContinuation";
3333
import {
3434
applyAutoDirectorResetStepReadiness,
3535
extractAutoDirectorResetStepsFromMeta,
@@ -100,6 +100,7 @@ function shouldShowBookAutomationProjectionWithoutActiveTask(input: {
100100
|| input.status === "waiting_approval"
101101
|| input.status === "waiting_recovery"
102102
|| input.status === "blocked"
103+
|| input.status === "failed"
103104
) {
104105
return true;
105106
}
@@ -118,26 +119,6 @@ function shouldShowBookAutomationProjectionWithoutActiveTask(input: {
118119
});
119120
}
120121

121-
function resolveDirectorContinueMode(task: Pick<
122-
UnifiedTaskDetail,
123-
"checkpointType" | "currentItemKey" | "currentStage" | "pendingManualRecovery"
124-
> | null | undefined): DirectorContinuationMode {
125-
if (task?.pendingManualRecovery) {
126-
return "resume";
127-
}
128-
if (
129-
task?.checkpointType === "replan_required"
130-
|| task?.currentItemKey === "quality_repair"
131-
|| task?.currentStage?.includes("质量")
132-
) {
133-
return "skip_quality_repair";
134-
}
135-
if (task?.checkpointType === "chapter_batch_ready") {
136-
return "auto_execute_range";
137-
}
138-
return "resume";
139-
}
140-
141122
export default function NovelWorkspaceRail(props: NovelWorkspaceRailProps) {
142123
const { novelId, chapterId = "", collapsed, onToggle, onSwitchToProjectNav } = props;
143124
const navigate = useNavigate();

‎client/src/components/layout/Sidebar.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const navGroups: NavGroup[] = [
6262
{ to: "/story-modes", label: "推进模式库", icon: Workflow },
6363
{ to: "/titles", label: "标题工坊", icon: SquarePen },
6464
{ to: "/knowledge", label: "知识库", icon: Database },
65-
{ to: "/worlds", label: "世界观", icon: Globe2 },
65+
{ to: "/worlds", label: "世界样本库", icon: Globe2 },
6666
{ to: "/style-engine", label: "写法引擎", icon: WandSparkles },
6767
{ to: "/anti-ai-rules", label: "反 AI 规则", icon: ShieldCheck },
6868
{ to: "/base-characters", label: "基础角色库", icon: UsersRound },

0 commit comments

Comments
 (0)