@waterwx/dsh-novel-forge 1.1.0 → 1.1.2

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/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/protocol.ts","../src/docx.ts","../src/assets.ts","../src/engine.ts","../src/assistant.ts","../src/bookshelf.ts","../src/routes.ts","../src/index.ts"],"sourcesContent":["/**\n * dsh-novel-forge — shared protocol between the host half (Node) and the\n * browser half (web GUI). Route paths, request/response shapes, the project\n * state file format, and the NDJSON generation stream frames all live here so\n * both halves spell exactly one vocabulary.\n */\n\n/** The /api/dsh-novel-forge route family (same-origin, loopback-fenced). */\nexport const NOVEL_API = {\n status: '/api/dsh-novel-forge/status',\n loadOutline: '/api/dsh-novel-forge/load-outline',\n saveOutline: '/api/dsh-novel-forge/save-outline',\n plan: '/api/dsh-novel-forge/plan',\n volumes: '/api/dsh-novel-forge/volumes',\n bible: '/api/dsh-novel-forge/bible',\n assets: '/api/dsh-novel-forge/assets',\n styleEngine: '/api/dsh-novel-forge/style-engine',\n generate: '/api/dsh-novel-forge/generate',\n review: '/api/dsh-novel-forge/review',\n rewrite: '/api/dsh-novel-forge/rewrite',\n polish: '/api/dsh-novel-forge/polish',\n /** 采纳待确认草稿(润色/重写产物)覆盖正文文件。 */\n draftApply: '/api/dsh-novel-forge/draft/apply',\n /** 放弃待确认草稿,保留原稿。 */\n draftDiscard: '/api/dsh-novel-forge/draft/discard',\n summary: '/api/dsh-novel-forge/summary',\n foreshadow: '/api/dsh-novel-forge/foreshadow',\n exportBook: '/api/dsh-novel-forge/export',\n chapter: '/api/dsh-novel-forge/chapter',\n /** 审查任意正文文本(作者手动编辑后,不落盘)。 */\n chapterCheck: '/api/dsh-novel-forge/chapter/check',\n /** 保存手动编辑的正文(自动备份 .bak)。 */\n chapterSave: '/api/dsh-novel-forge/chapter/save',\n assistant: '/api/dsh-novel-forge/assistant',\n assistantHistory: '/api/dsh-novel-forge/assistant-history',\n /** 清空助手对话记录。 */\n assistantClear: '/api/dsh-novel-forge/assistant/clear',\n bookshelf: '/api/dsh-novel-forge/bookshelf',\n /** 重置项目(可选携带新大纲):清空设定/卷/章节/伏笔/资产/事实库。 */\n reset: '/api/dsh-novel-forge/reset',\n /** 全书一致性质检:LLM 扫描已生成章节,输出矛盾问题清单。 */\n audit: '/api/dsh-novel-forge/audit',\n /** 角色卡刷新:基于事实库与各章摘要聚合角色当前状态。 */\n charactersRefresh: '/api/dsh-novel-forge/characters/refresh',\n /** 事实库回填:对历史已生成章节批量抽取事实(旧章节无事实记录时用)。 */\n factsBackfill: '/api/dsh-novel-forge/facts/backfill',\n /** 设定圣经局部修补(如世界观规则编辑)。 */\n biblePatch: '/api/dsh-novel-forge/bible/patch',\n /** 小说简介:生成(AI)/补全(AI)/保存。 */\n blurb: '/api/dsh-novel-forge/blurb',\n /** 重命名当前书(同步项目与书架条目)。 */\n rename: '/api/dsh-novel-forge/rename',\n /** 大世界:AI 提炼 / 保存结构化数据(境界/区域/势力)。 */\n world: '/api/dsh-novel-forge/world',\n /** 封面:GET 读取(dataUrl)/ POST 上传或移除。 */\n cover: '/api/dsh-novel-forge/blurb/cover',\n config: '/api/dsh-novel-forge/config',\n openFolder: '/api/dsh-novel-forge/open-folder',\n} as const\n\n/** 书架:一本书的条目。 */\nexport interface BookEntry {\n /** 稳定 id。 */\n id: string\n /** 书名。 */\n bookName: string\n /** 该书输出目录(独立项目目录)。 */\n outputDir: string\n /** 创建时间。 */\n createdAt: string\n /** 最后活动时间。 */\n updatedAt: string\n}\n\n/** 书架快照(含每本书的进度摘要)。 */\nexport interface BookshelfSnapshot {\n books: Array<BookEntry & { done: number; total: number; hasProject: boolean; hasCover: boolean; blurb?: string }>\n /** 当前激活的书 id(无则 null)。 */\n activeBookId: string | null\n}\n\n/** POST /bookshelf 请求:创建新书。 */\nexport interface BookCreateRequest {\n bookName: string\n outputDir?: string\n /** 开书向导:创建时直接导入的大纲文本(提供则立即建立项目)。 */\n outline?: string\n}\n\n/** POST /reset 请求:重置项目(可选更新大纲)。 */\nexport interface ResetRequest {\n /** 新大纲文本;提供则替换 outline,否则保留原大纲。 */\n outline?: string\n}\n\n/** POST /bookshelf/activate 请求:切换当前书。 */\nexport interface BookActivateRequest {\n id: string\n}\n\n/** POST /bookshelf/remove 请求:移除书架条目。 */\nexport interface BookRemoveRequest {\n id: string\n}\n\n/** Chapter lifecycle states (the writing pipeline's state machine). */\nexport type ChapterStatus =\n | 'pending' // planned, not started\n | 'generating' // LLM writing right now\n | 'written' // body on disk, awaiting review\n | 'reviewing' // review in progress\n | 'approved' // passed review (or user-approved)\n | 'rejected' // review found problems\n | 'error' // generation failed\n\n/** One chapter in the plan. */\nexport interface ChapterPlan {\n /** 1-based chapter number (stable identity; files are named from it). */\n no: number\n /** Volume this chapter belongs to (1-based; 0 = unassigned). */\n volume: number\n /** Chapter title, decided by the LLM plan step. */\n title: string\n /** Story beats / plot points for this chapter (model-facing guidance). */\n beats: string\n /** Target character count (defaults to the configured chapter size). */\n targetChars: number\n /** Generation/review state. */\n status: ChapterStatus\n /** Actual character count once generated. */\n chars?: number\n /** Failure message when status is 'error'. */\n error?: string\n /** Output file name once generated (relative to the output dir). */\n file?: string\n /** LLM summary of the chapter (narrative memory for later chapters). */\n summary?: string\n /** Latest review report (present once reviewed). */\n review?: ReviewReport\n /**\n * 待确认草稿:润色(去AI味)或整章重写的产物正文。生成时先存这里,\n * 用户看过对比后点「采纳」才覆盖正文文件;点「放弃」则丢弃。刷新页面不丢失。\n */\n pendingDraft?: string\n}\n\n/** One review finding. */\nexport interface ReviewIssue {\n /** Severity: high = must fix, medium = should fix, low = suggestion. */\n severity: 'high' | 'medium' | 'low'\n /** What the problem is. */\n item: string\n /** Concrete suggestion for fixing it. */\n suggestion: string\n}\n\n/** AI review report for one chapter. */\nexport interface ReviewReport {\n /** Overall score 0-100. */\n score: number\n /** Pass threshold (config; 70 default). */\n passed: boolean\n /** One-line verdict. */\n verdict: string\n /** Individual findings. */\n issues: ReviewIssue[]\n /** When the review ran. */\n reviewedAt: string\n}\n\n/** A volume of the book. */\nexport interface Volume {\n /** 1-based volume number. */\n no: number\n /** Volume title. */\n title: string\n /** Volume positioning / summary. */\n summary: string\n /** First chapter number of this volume. */\n chapterStart: number\n /** Last chapter number (inclusive). */\n chapterEnd: number\n}\n\n/** A character card from the story bible. */\nexport interface CharacterCard {\n name: string\n role: 'protagonist' | 'supporting' | 'antagonist' | 'other'\n /** Personality / traits (short lines). */\n traits: string[]\n /** Goals and motivations. */\n goals: string\n /** Key relations to other characters. */\n relations: string\n}\n\n/** The structured story bible (worldbuilding extracted from the outline). */\nexport interface StoryBible {\n /** Genre + tone tags. */\n genre: string\n /** Worldbuilding rules (power system, geography, factions...). */\n worldRules: string[]\n /** Character cards. */\n characters: CharacterCard[]\n /** Writing red lines (forbidden content / must-avoid tropes). */\n redLines: string[]\n /** Style guidance (pacing, pov, tone). */\n style: string[]\n /** When the bible was generated. */\n generatedAt?: string\n}\n\n/** A planted/active/resolved foreshadowing thread. */\nexport interface Foreshadow {\n /** Stable id. */\n id: string\n /** What the foreshadow is. */\n description: string\n /** Chapter where it was planted (undefined = planned). */\n plantedChapter?: number\n /** Chapter where it should be paid off. */\n targetChapter?: number\n /** Lifecycle state. */\n status: 'planned' | 'planted' | 'progressing' | 'resolved' | 'abandoned'\n /** Resolution note when resolved. */\n resolvedNote?: string\n}\n\n/** 一条已确立的叙事事实(事实库/时间线,注入后续章节生成)。 */\nexport interface ChapterFact {\n /** 来源章节号。 */\n chapterNo: number\n /** 事实文本(人物状态/境界资源/关系变化/伏笔落地等)。 */\n text: string\n}\n\n/** 一条全书质检发现的问题(一致性矛盾,定位到章)。 */\nexport interface AuditIssue {\n /** 问题所在章节号(无法定位时 0)。 */\n chapterNo: number\n severity: 'high' | 'medium' | 'low'\n /** 矛盾描述。 */\n item: string\n /** 修改建议。 */\n suggestion: string\n}\n\n/** POST /audit 响应。 */\nexport interface AuditResponse {\n issues: AuditIssue[]\n /** 参与质检的章节数。 */\n auditedChapters: number\n /** 质检时间。 */\n auditedAt: string\n}\n\n/** 角色卡:角色当前状态(从事实库聚合)。 */\nexport interface RoleStatusCard {\n name: string\n /** protagonist / supporting / antagonist / other。 */\n role: string\n /** 当前状态一句话(境界/资源/伤势/心境)。 */\n status: string\n /** 最近出场章节。 */\n lastChapter: number\n /** 出场次数。 */\n appearances: number\n}\n\n/** POST /bible/patch 请求:局部修补设定圣经。 */\nexport interface BiblePatchRequest {\n worldRules?: string[]\n redLines?: string[]\n style?: string[]\n}\n\n/** POST /blurb 请求:AI 生成/补全或手动保存小说简介。 */\nexport interface BlurbRequest {\n action: 'generate' | 'save'\n /** 已写好的开头(AI 补全时使用;留空 = 全量生成)。 */\n partial?: string\n /** 手动保存的完整简介(action=save 时)。 */\n text?: string\n}\n\n/** POST /chapter/check|save 请求:审查/保存手动编辑的正文。 */\nexport interface ChapterTextRequest {\n chapterNo: number\n /** 当前编辑中的正文全文。 */\n text: string\n /** 保存时携带:已在工作区审查过的报告(沿用落盘,不重复审);缺省则保存后自动正式审稿一次。 */\n report?: ReviewReport\n}\n\n/** POST /chapter/save 响应。 */\nexport interface ChapterSaveResponse {\n ok: boolean\n chars: number\n file: string\n /** 落盘的审稿报告(沿用工作区报告或保存后自动审稿)。 */\n report?: ReviewReport\n}\n\n/** POST /cover 请求:上传或移除封面。 */\nexport interface CoverRequest {\n action: 'upload' | 'remove'\n /** 上传时:data:image/...;base64,... 格式的图片数据。 */\n dataUrl?: string\n}\n\n/** POST /rename 请求:重命名当前书。 */\nexport interface RenameRequest {\n bookName: string\n}\n\n/** 大世界:一个境界等级。 */\nexport interface WorldRealm {\n /** 境界名(练气/筑基/金丹…)。 */\n name: string\n /** 描述:突破条件/寿命/标志等。 */\n description: string\n}\n\n/** 大世界:一个地理区域。 */\nexport interface WorldRegion {\n name: string\n description: string\n /** 关联势力名(可空)。 */\n faction?: string\n}\n\n/** 大世界:一方势力。 */\nexport interface WorldFaction {\n name: string\n /** 类型:宗门/家族/王朝/组织… */\n kind: string\n description: string\n /** 驻地区域(可空)。 */\n region?: string\n}\n\n/** 大世界结构化数据。 */\nexport interface WorldState {\n realms: WorldRealm[]\n regions: WorldRegion[]\n factions: WorldFaction[]\n}\n\n/** POST /world 请求:AI 提炼或手动保存。 */\nexport interface WorldRequest {\n action: 'generate' | 'save'\n /** action=save 时的完整世界数据。 */\n world?: WorldState\n}\n\n/** GET /cover 响应:封面的 dataUrl(无封面为 null)。 */\nexport interface CoverResponse {\n dataUrl: string | null\n}\n\n/** The persisted project: outline + bible + plan + progress. */\nexport interface ProjectState {\n /** Book title (first non-empty line of the outline, usually). */\n bookName: string\n /** Full outline text (docx-extracted or pasted). */\n outline: string\n /** Source outline path when loaded from a docx. */\n outlinePath?: string\n /** Structured story bible (worldbuilding), if generated. */\n bible?: StoryBible\n /** Volumes, if planned. */\n volumes?: Volume[]\n /** Chapter plan. */\n chapters: ChapterPlan[]\n /** Foreshadowing threads. */\n foreshadows: Foreshadow[]\n /** 写作资产(题材基底/推进模式/反AI规则/写法资产)。 */\n assets?: ProjectAssets\n /** 事实库/时间线:每章生成后抽取,注入后续章节保持一致性。 */\n facts?: ChapterFact[]\n /** 小说简介(面向读者的作品门面,AI 生成或手动保存)。 */\n blurb?: string\n /** 封面文件名(相对输出目录,如 cover.png)。 */\n coverPath?: string\n /** 大世界结构化数据(境界体系/区域/势力)。 */\n world?: WorldState\n /** ISO timestamps. */\n createdAt: string\n updatedAt: string\n}\n\n/** Runtime config surface exposed to the panel (subset of plugin Config). */\nexport interface NovelConfig {\n /** Absolute path of the default docx outline to load. */\n outlinePath: string\n /** Absolute output directory for chapters + project state. */\n outputDir: string\n /** LLM provider route (e.g. deepseek-official). */\n provider: string\n /** LLM model id (e.g. deepseek-v4-flash). */\n model: string\n /** Target characters per chapter. */\n chapterChars: number\n /** Max output tokens per chapter call. */\n maxTokens: number\n /** Review pass threshold (0-100). */\n reviewPassScore: number\n /** Whether generation auto-runs review after writing. */\n autoReview: boolean\n}\n\n/** GET /status response. */\nexport interface StatusResponse {\n config: NovelConfig\n /** The persisted project, when one exists in the output dir. */\n project?: ProjectState\n /** Chapter files already on disk (basenames, sorted). */\n generatedFiles: string[]\n}\n\n/** POST /load-outline request: either a docx path or raw text. */\nexport interface LoadOutlineRequest {\n /** Absolute docx path; defaults to the configured outline path. */\n path?: string\n /** Raw outline text (takes precedence over path when present). */\n text?: string\n}\n\n/** POST /load-outline response. */\nexport interface LoadOutlineResponse {\n outline: string\n bookName: string\n chars: number\n path?: string\n}\n\n/** POST /plan request. */\nexport interface PlanRequest {\n /** Outline to plan from; defaults to the persisted project's outline. */\n outline?: string\n /** Number of chapters to plan (default: 30). */\n chapterCount?: number\n /** Volume to plan (1-based); when given, plans only that volume's chapters. */\n volume?: number\n}\n\n/** POST /plan response. */\nexport interface PlanResponse {\n chapters: ChapterPlan[]\n volumes?: Volume[]\n}\n\n/** POST /volumes request/response. */\nexport interface VolumesRequest {\n /** Outline to split into volumes; defaults to the project outline. */\n outline?: string\n}\nexport interface VolumesResponse {\n volumes: Volume[]\n}\n\n/** POST /bible request/response. */\nexport interface BibleRequest {\n /** Outline to extract from; defaults to the project outline. */\n outline?: string\n}\nexport interface BibleResponse {\n bible: StoryBible\n}\n\n/** POST /generate request: one chapter of the current project. */\nexport interface GenerateRequest {\n chapterNo: number\n /** When true, skips the auto-review step. */\n skipReview?: boolean\n}\n\n/** One NDJSON frame of a generation/review/rewrite stream. */\nexport type JobFrame =\n | { type: 'start'; no: number; title: string }\n | { type: 'delta'; text: string }\n | { type: 'progress'; chars: number }\n | { type: 'done'; no: number; file: string; chars: number; title: string }\n | { type: 'review'; no: number; report: ReviewReport }\n | { type: 'rewritten'; no: number; file: string; chars: number }\n /** 润色/重写完成,产物作为待确认草稿(尚未覆盖正文)。 */\n | { type: 'drafted'; no: number; chars: number; draft: string }\n | { type: 'error'; no: number; message: string }\n\n/** POST /review request: review one written chapter. */\nexport interface ReviewRequest {\n chapterNo: number\n}\n\n/** POST /rewrite request: rewrite one chapter (optionally per review issues). */\nexport interface RewriteRequest {\n chapterNo: number\n /** Free-form instructions; defaults to fixing the review's high issues. */\n instructions?: string\n /**\n * 局部修订:正文中的一段原文(无需完全精确,取一个自然段内的片段即可)。\n * 提供时只重写该段,其余正文保持不变;不提供时整章重写。\n */\n target?: string\n}\n\n/** POST /polish request: de-AI-ify one chapter. */\nexport interface PolishRequest {\n chapterNo: number\n}\n\n/** POST /draft/apply | /draft/discard request: 采纳或放弃待确认草稿。 */\nexport interface DraftDecisionRequest {\n chapterNo: number\n}\n\n/** POST /summary request: (re)generate a chapter summary. */\nexport interface SummaryRequest {\n chapterNo: number\n}\n\n/** POST /foreshadow request: create, update, or AI-suggest foreshadows. */\nexport interface ForeshadowRequest {\n /** When true, runs the LLM suggestion pass (ignores other fields). */\n suggest?: boolean\n /** When given, updates that foreshadow instead of creating one. */\n id?: string\n description?: string\n plantedChapter?: number\n targetChapter?: number\n status?: Foreshadow['status']\n resolvedNote?: string\n}\nexport interface ForeshadowResponse {\n foreshadows: Foreshadow[]\n}\n\n/** GET /chapter response. */\nexport interface ChapterResponse {\n no: number\n title: string\n markdown: string\n}\n\n/** POST /export request/response. */\nexport interface ExportRequest {\n format: 'txt' | 'md'\n}\nexport interface ExportResponse {\n file: string\n chars: number\n chapters: number\n}\n\n/** POST /config request: patch any subset of the runtime config. */\nexport interface ConfigPatch {\n outlinePath?: string\n outputDir?: string\n provider?: string\n model?: string\n chapterChars?: number\n maxTokens?: number\n reviewPassScore?: number\n autoReview?: boolean\n}\n\n// ------------------------------------------------------------ assistant\n\n/** One assistant conversation message (persisted per project). */\nexport interface AssistantMessage {\n role: 'user' | 'assistant' | 'tool'\n /** Message text (tool messages carry the tool result). */\n content: string\n /** ISO timestamp. */\n ts: string\n /** For tool messages: which tool ran. */\n tool?: string\n}\n\n/** POST /assistant request: one user turn. */\nexport interface AssistantRequest {\n message: string\n}\n\n/** One NDJSON frame of the assistant stream. */\nexport type AssistantFrame =\n | { type: 'delta'; text: string }\n | { type: 'tool'; name: string; status: 'start' | 'done' | 'error'; detail?: string }\n /** Live output while a tool runs (e.g. chapter text being generated). */\n | { type: 'toolDelta'; name: string; text: string }\n | { type: 'done' }\n | { type: 'error'; message: string }\n\n/** GET /assistant-history response. */\nexport interface AssistantHistoryResponse {\n messages: AssistantMessage[]\n}\n\n// ---------------------------------------------------------- writing assets\n\n/** 题材基底库:一本书属于哪个阅读市场。树形(题材→子题材→下级)。 */\nexport interface GenreNode {\n /** 题材名称(标签,如「仙侠修真」「都市异能」)。 */\n name: string\n /** 题材特征、常见爽点、叙事重心或读者期待。 */\n description: string\n /** 子题材。 */\n children: GenreNode[]\n}\n\n/** 推进模式库:读者为什么继续看下一章。 */\nexport interface ProgressionMode {\n /** 模式名称(如「升级变强」「经营扩张」「解谜揭露」)。 */\n name: string\n /** 核心驱动力:靠什么制造追读动力。 */\n driver: string\n /** 读者期待:每隔几章获得什么变化或回报。 */\n readerExpectation: string\n /** 常见兑现方式(爽点如何落地)。 */\n payoffs: string[]\n /** 节奏风险:最怕什么(重复升级、冲突变弱、谜题拖太久…)。 */\n risks: string[]\n /** 主模式或辅助模式。 */\n primary: boolean\n}\n\n/** 反 AI 规则:一条「要避免的问题 + 修正方向」。 */\nexport interface AntiAiRule {\n /** 规则名(如「禁止解释型心理描写」「AI 高频套话」)。 */\n name: string\n /** 要避免的表达问题,具体可检查。 */\n avoid: string\n /** 推荐修正方向。 */\n fix: string\n /** 命中即告警的具体表达模式(用于审稿逐条核对与去 AI 味检测)。 */\n detectPatterns?: string[]\n /** 是否内置全局规则(内置规则随插件发布,项目规则为用户自定义)。 */\n builtin?: boolean\n}\n\n/** 预置写法模板(来自 AI-Novel-Writing-Assistant 内置数据,一键绑定无需样本文本)。 */\nexport interface StyleTemplate {\n /** 模板 key(如 power-up-escalation)。 */\n key: string\n /** 模板名(如「爽文递进推进流」)。 */\n name: string\n /** 模板说明。 */\n description: string\n /** 分类(如「爽文流」「悬疑流」)。 */\n category: string\n /** 适用题材。 */\n applicableGenres: string[]\n /** 叙述规则。 */\n proseRules: string[]\n /** 角色/台词规则。 */\n dialogueRules: string[]\n /** 语言规则。 */\n languageRules: string[]\n /** 节奏规则。 */\n rhythmRules: string[]\n /** 该模板默认绑定的反 AI 规则 key(内置规则名)。 */\n defaultAntiAiRuleKeys: string[]\n}\n\n/** 写法引擎:从样本文本提取的叙事风格资产。 */\nexport interface StyleAsset {\n /** 资产名(如「林越式痞坏」「冷峻猎手风」)。 */\n name: string\n /** 叙述视角与句式节奏。 */\n proseRules: string[]\n /** 角色台词风格。 */\n dialogueRules: string[]\n /** 描写密度与情绪表达。 */\n descriptionRules: string[]\n /** 表达边界(不要做什么)。 */\n boundaries: string[]\n /** 来源样本文本(可空)。 */\n sourceText?: string\n /** 创建时间。 */\n createdAt: string\n}\n\n/** 项目写作资产(题材基底 + 推进模式 + 反 AI 规则 + 写法资产)。 */\nexport interface ProjectAssets {\n /** 本书选用的题材(可以是题材基底库中某节点名)。 */\n genre?: GenreNode\n /** 主推进模式。 */\n primaryProgression?: ProgressionMode\n /** 辅助推进模式。 */\n auxiliaryProgressions: ProgressionMode[]\n /** 生效的反 AI 规则(内置 + 自定义)。 */\n antiAiRules: AntiAiRule[]\n /** 绑定的写法资产。 */\n styleAssets: StyleAsset[]\n /** 资产更新时间。 */\n updatedAt?: string\n}\n\n/** GET /assets response(含全局题材库与反 AI 规则库)。 */\nexport interface AssetsResponse {\n projectAssets: ProjectAssets\n /** 全局题材基底库(可复用资产,跨书)。 */\n genreLibrary: GenreNode[]\n /** 全局反 AI 规则库(内置默认规则)。 */\n antiAiLibrary: AntiAiRule[]\n /** 预置写法模板(一键绑定,无需样本文本)。 */\n styleTemplates: StyleTemplate[]\n /** 内置推进模式候选。 */\n progressionLibrary: ProgressionMode[]\n}\n\n/** POST /assets request:更新项目写作资产(部分字段可选)。 */\nexport interface AssetsPatch {\n genre?: GenreNode\n primaryProgression?: ProgressionMode\n auxiliaryProgressions?: ProgressionMode[]\n antiAiRules?: AntiAiRule[]\n styleAssets?: StyleAsset[]\n}\n\n/** POST /style-engine request:从样本文本提取写法资产。 */\nexport interface StyleEngineRequest {\n /** 样本文本(风格来源)。 */\n sampleText: string\n /** 资产名(可选,默认「风格资产 N」)。 */\n name?: string\n}\n","/**\n * docx outline extraction: a .docx is a zip whose word/document.xml holds the\n * body text in <w:t> runs inside <w:p> paragraphs. We unzip with fflate and\n * walk the XML with a tiny tokenizer — no heavyweight XML/DOM dependency.\n */\n\nimport { readFileSync } from 'node:fs'\nimport { unzipSync, strFromU8 } from 'fflate'\n\n/** Decode the handful of XML entities docx bodies actually use. */\nfunction decodeEntities(text: string): string {\n return text\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&apos;/g, \"'\")\n .replace(/&amp;/g, '&')\n .replace(/&nbsp;/g, ' ')\n}\n\n/**\n * Extract plain text from a docx buffer: one line per <w:p> paragraph, with\n * <w:tab>/<w:br> preserved as whitespace. Tables and nested structures are\n * flattened in document order (their paragraphs are just <w:p> too).\n * @param buffer - the raw .docx bytes.\n * @returns the body text.\n */\nexport function extractDocxText(buffer: Uint8Array): string {\n let files: ReturnType<typeof unzipSync>\n try {\n files = unzipSync(buffer)\n } catch (error) {\n throw new Error(`not a valid docx (zip open failed): ${(error as Error).message}`)\n }\n const document = files['word/document.xml']\n if (document === undefined) {\n throw new Error('not a valid docx (word/document.xml missing)')\n }\n const xml = strFromU8(document)\n\n const paragraphs: string[] = []\n // Split on paragraph boundaries; keep the segment text of each.\n const parts = xml.split(/<w:p\\b[^>]*>/)\n for (let i = 1; i < parts.length; i++) {\n const segment = parts[i]!\n // Runs <w:t ...>…</w:t>; also honor <w:tab/> and <w:br/> as spaces.\n const runs: string[] = []\n const runRe = /<w:t(?:\\s[^>]*)?>([\\s\\S]*?)<\\/w:t>|<w:tab\\b[^>]*\\/>|<w:br\\b[^>]*\\/>/g\n for (const match of segment.matchAll(runRe)) {\n if (match[0].startsWith('<w:tab')) {\n runs.push('\\t')\n } else if (match[0].startsWith('<w:br')) {\n runs.push('\\n')\n } else {\n runs.push(decodeEntities(match[1] ?? ''))\n }\n }\n const line = runs.join('').replace(/\\u00a0/g, ' ').trimEnd()\n paragraphs.push(line)\n }\n\n // Collapse 3+ blank lines and trim.\n const text = paragraphs.join('\\n').replace(/\\n{3,}/g, '\\n\\n').trim()\n if (text.length === 0) {\n throw new Error('docx contains no extractable text')\n }\n return text\n}\n\n/**\n * Read and extract a docx outline from disk.\n * @param path - absolute path to the .docx file.\n * @returns the extracted outline text.\n */\nexport function readOutlineFromDocx(path: string): string {\n let buffer: Buffer\n try {\n buffer = readFileSync(path)\n } catch (error) {\n throw new Error(`cannot read outline file \"${path}\": ${(error as Error).message}`)\n }\n return extractDocxText(new Uint8Array(buffer))\n}\n","/**\n * Writing assets — 题材基底库 / 推进模式库 / 反 AI 规则 / 写法引擎.\n *\n * Ported from AI-Novel-Writing-Assistant (Apache-2.0) built-in seed data:\n * - 8 preset style templates (DEFAULT_STYLE_TEMPLATES)\n * - 12 anti-AI rules with concrete detect patterns (DEFAULT_ANTI_AI_RULES)\n * - genre tree + progression mode seeds\n * Assets persist with the project (novel-project.json) and are injected into\n * generation / planning / review prompts, and available to the AI assistant.\n */\n\nimport type { AntiAiRule, GenreNode, ProgressionMode, ProjectAssets, StyleAsset, StyleTemplate } from './protocol.ts'\n\n// ------------------------------------------------------ built-in style templates\n\n/** 预置写法模板(来自 AI-Novel-Writing-Assistant 内置 DEFAULT_STYLE_TEMPLATES)。 */\nexport const BUILTIN_STYLE_TEMPLATES: StyleTemplate[] = [\n {\n key: 'power-up-escalation',\n name: '爽文递进推进流',\n description: '持续升级冲突和收益点,强化目标推进与爽点兑现。',\n category: '爽文流',\n applicableGenres: ['都市', '玄幻', '热血'],\n proseRules: [\n '围绕目标推进,尽快兑现局部收益;每段都要有目标推进或爽点兑现。',\n '保持明确因果和节奏抬升,场景单元按「目标→阻碍→压制→反转收益」推进。',\n '优先冲突和结果,少停留;段尾用钩子收束。',\n ],\n dialogueRules: [\n '角色表达直接,情绪跟随胜负切换。',\n '对话承担推进与信息功能,但保留角色自己的语气差异。',\n ],\n languageRules: [\n '句式清晰,减少无效分散信息。',\n '直接、明确,不做无谓铺垫。',\n ],\n rhythmRules: [\n '快节奏,段落密度中等,动作先于解释。',\n '尽快兑现局部收益,避免拖沓。',\n ],\n defaultAntiAiRuleKeys: ['禁止总结主题', '对话纯功能推进', '连续三段解释性叙事'],\n },\n {\n key: 'bottom-loop-reality',\n name: '底层循环现实流',\n description: '通过碎片化生活与反复落空表现人物困境。',\n category: '现实流',\n applicableGenres: ['都市', '现实', '成长'],\n proseRules: [\n '以时间推进和现实落差构成叙事张力,结尾不解决核心困境。',\n '场景单元按「行为→落差→自我合理化」推进。',\n '以碎片化生活推进,不做总括式回顾。',\n ],\n dialogueRules: [\n '人物情绪通过动作和嘴硬表达,允许短促口语化台词。',\n '对话保留生活杂音与无效信息。',\n ],\n languageRules: [\n '语言粗粝、口语化,允许生活杂音与不完整句。',\n '句子变化度高,允许无意义细节。',\n ],\n rhythmRules: [\n '段落密实,动作先于解释。',\n '中快节奏,允许碎片化流动。',\n ],\n defaultAntiAiRuleKeys: ['禁止解释型心理描写', '禁止段尾升华', '鼓励无意义小动作', '鼓励现实落差', '鼓励嘴硬补偿'],\n },\n {\n key: 'suspense-pressure',\n name: '悬疑压迫递增流',\n description: '通过信息遮蔽、细节异常和压力叠加制造不安感。',\n category: '悬疑流',\n applicableGenres: ['悬疑', '惊悚', '现实'],\n proseRules: [\n '以异常细节、信息差和节奏收束推动悬念层层加压。',\n '场景单元按「现场细节→异常→误判→新风险」推进。',\n '优先制造信息缺口和压迫氛围。',\n ],\n dialogueRules: [\n '角色反应克制,恐惧通过反应显现。',\n '对话保留克制感,不解释恐惧来源。',\n ],\n languageRules: [\n '细节精确,保留少量噪音增强现场感。',\n '克制、中等偏高句变化。',\n ],\n rhythmRules: [\n '通过节奏收束和信息延迟制造压力。',\n '中速,段落密度中等偏高。',\n ],\n defaultAntiAiRuleKeys: ['禁止解释型心理描写', '禁止总结主题', '段落长度过于整齐', '鼓励现实落差'],\n },\n {\n key: 'emotional-tension',\n name: '情绪拉扯流',\n description: '通过错位表达、停顿和误读制造关系张力。',\n category: '情感流',\n applicableGenres: ['言情', '都市', '群像'],\n proseRules: [\n '人物不直说核心情绪,靠误读、停顿和反应推动关系变化。',\n '场景单元按「动作→言外之意→误读→回避」推进。',\n '以关系错位推进,而非直接说明。',\n ],\n dialogueRules: [\n '情绪通过停顿、动作和言外之意体现。',\n '对话充满潜台词与试探。',\n ],\n languageRules: [\n '语言自然,允许留白与停顿。',\n '句子变化度高,允许无意义细节。',\n ],\n rhythmRules: [\n '给关系反应留空间,但避免空洞抒情。',\n '中慢节奏,段落密度中等。',\n ],\n defaultAntiAiRuleKeys: ['禁止直接说教', '禁止段尾升华', '对话纯功能推进', '鼓励无意义小动作'],\n },\n {\n key: 'ensemble-weave',\n name: '群像交织流',\n description: '以多人行动线和视角差异交织推进事件。',\n category: '群像流',\n applicableGenres: ['群像', '都市', '悬疑'],\n proseRules: [\n '多角色并行推进,但每个角色的表达和认知范围必须区分清楚。',\n '多线并进,但视角切换要受控。',\n ],\n dialogueRules: [\n '不同角色口吻必须拉开差异,避免所有人说话一样。',\n ],\n languageRules: [\n '保持角色差异,句式变化度高。',\n '减少无效分散信息。',\n ],\n rhythmRules: [\n '多线交织但节奏不乱,平衡推进。',\n '动作先于解释。',\n ],\n defaultAntiAiRuleKeys: ['对话纯功能推进', '句式重复率过高', '禁止总结主题'],\n },\n {\n key: 'immersive-daily',\n name: '日常浸没流',\n description: '通过生活细节和细微情绪变化建立持续沉浸感。',\n category: '日常流',\n applicableGenres: ['日常', '治愈', '都市'],\n proseRules: [\n '重场景体验和关系温度,核心情绪通过场景自然流出。',\n '允许保留生活性动作和无效信息。',\n ],\n dialogueRules: [\n '人物表达自然,不用高强度戏剧句。',\n '对话保留生活气息。',\n ],\n languageRules: [\n '保留生活细节和杂音,不追求工整。',\n '口语化,句子变化中等偏高。',\n ],\n rhythmRules: [\n '慢节奏沉浸,但避免空转。',\n '允许碎片化流动。',\n ],\n defaultAntiAiRuleKeys: ['禁止段尾升华', '段落长度过于整齐', '鼓励无意义小动作'],\n },\n {\n key: 'cold-professional',\n name: '冷峻专业流',\n description: '以专业事实和行业细节压住情绪,形成克制压力感。',\n category: '专业流',\n applicableGenres: ['职场', '现实', '悬疑'],\n proseRules: [\n '行业事实和程序细节优先,情绪不直说,信息密度高于抒情密度。',\n '场景单元按「事实→动作→专业判断→后果」推进。',\n '让专业事实承担叙事重量。',\n ],\n dialogueRules: [\n '情绪藏在专业动作和事实选择里。',\n '对话以信息性表达为主,克制。',\n ],\n languageRules: [\n '术语和事实优先,避免廉价金句。',\n '正式、克制的语言。',\n ],\n rhythmRules: [\n '信息密度高,但不铺张解释。',\n '平衡节奏,段落密度中等偏高。',\n ],\n defaultAntiAiRuleKeys: ['禁止直接说教', '禁止总结主题', '句式重复率过高'],\n },\n {\n key: 'absurd-dark-humor',\n name: '荒诞黑色幽默流',\n description: '通过反差、冷感观察和荒诞细节制造黑色幽默。',\n category: '黑色幽默',\n applicableGenres: ['都市', '黑色幽默', '现实'],\n proseRules: [\n '用反差和荒诞细节放大现实困境,笑点和压迫感同时存在。',\n '场景单元按「现实细节→荒诞偏差→冷反应」推进。',\n '依赖反差和冷感观察,而非热闹吐槽。',\n ],\n dialogueRules: [\n '情绪藏在冷反应和嘴硬里。',\n '台词冷面、口语化,允许自嘲与转移。',\n ],\n languageRules: [\n '允许夹带荒诞杂质和冷幽默节奏。',\n '口语化,句子变化度高。',\n ],\n rhythmRules: [\n '反差点要快落地,不要解释笑点。',\n '平衡节奏,段落密度中等偏高。',\n ],\n defaultAntiAiRuleKeys: ['禁止解释型心理描写', '禁止段尾升华', '鼓励现实落差', '鼓励嘴硬补偿'],\n },\n]\n\n// ---------------------------------------------------------- built-in rules\n\n/** 内置全局反 AI 规则(来自 AI-Novel-Writing-Assistant 内置 DEFAULT_ANTI_AI_RULES)。 */\nexport const BUILTIN_ANTI_AI_RULES: AntiAiRule[] = [\n {\n name: '禁止解释型心理描写',\n avoid: '直接使用\"他感到\"\"他意识到\"\"他明白了\"等句式解释人物心理。',\n fix: '把心理解释改成动作、语气、停顿、环境反应或结果。',\n detectPatterns: ['他感到', '她感到', '他意识到', '她意识到', '他明白了', '她明白了'],\n builtin: true,\n },\n {\n name: '禁止段尾升华',\n avoid: '在段尾或收尾处用总结句升华主题(如\"生活就是\"\"命运总会\"\"说到底\")。',\n fix: '删除升华句,回到具体动作、现场或悬而未决的处境。',\n detectPatterns: ['生活就是', '命运总会', '归根结底', '说到底', '这就是'],\n builtin: true,\n },\n {\n name: '禁止总结主题',\n avoid: '把段落写成总结中心思想或提炼人生道理(如\"这说明\"\"这意味着\")。',\n fix: '删掉主题总结,让信息通过事件和结果自然显现。',\n detectPatterns: ['这说明', '这意味着', '归根结底', '其实就是'],\n builtin: true,\n },\n {\n name: '禁止直接说教',\n avoid: '作者替角色或读者做直接价值判断和说教(如\"我们都应该\"\"人总要学会\")。',\n fix: '改成角色具体处境或对话,不做抽象说教。',\n detectPatterns: ['我们都应该', '人总要学会', '真正重要的是'],\n builtin: true,\n },\n {\n name: '段落长度过于整齐',\n avoid: '段落长度和节奏过于平均,产生 AI 作文感。',\n fix: '打破段落长度均衡,让句子和段落有自然起伏。',\n detectPatterns: [],\n builtin: true,\n },\n {\n name: '连续三段解释性叙事',\n avoid: '连续几段只有解释没有动作,削弱现场感。',\n fix: '插入动作、对话、环境反馈,减少连段说明。',\n detectPatterns: [],\n builtin: true,\n },\n {\n name: '对话纯功能推进',\n avoid: '对话只有信息推进,没有人物语气和生活噪音(如\"告诉你\"\"我们现在要\")。',\n fix: '补入停顿、绕弯、语气差异和无效信息。',\n detectPatterns: ['告诉你', '我们现在要', '接下来就'],\n builtin: true,\n },\n {\n name: '句式重复率过高',\n avoid: '连续句式过于整齐(如\"首先\"\"然后\"\"接着\"\"最后\"),显得机械。',\n fix: '拉开句式长度和起句方式,打散结构。',\n detectPatterns: ['首先', '然后', '接着', '最后'],\n builtin: true,\n },\n {\n name: 'AI 高频套话',\n avoid: '滥用\"不禁\"\"仿佛\"\"一时间\"\"不由得\"\"顿时\"\"然而\"\"缓缓\"\"轻轻\"\"微微\"\"似乎\"\"终于\"等模式词及套路比喻。',\n fix: '用具体、有画面感的表达替换套话;每个比喻都应当是新造的。',\n detectPatterns: ['不禁', '仿佛', '一时间', '不由得', '顿时', '缓缓', '轻轻', '微微'],\n builtin: true,\n },\n {\n name: '鼓励无意义小动作',\n avoid: '(鼓励类)全篇缺少真实但不推动主线的小动作,人物显得空洞。',\n fix: '补入挠头、点烟、抠包装、挪椅子等小动作,增加人味与生活感。',\n detectPatterns: [],\n builtin: true,\n },\n {\n name: '鼓励现实落差',\n avoid: '(鼓励类)人物预期和现实结果完全一致,缺少落差。',\n fix: '补出人物预期与实际结果之间的差距,制造张力。',\n detectPatterns: [],\n builtin: true,\n },\n {\n name: '鼓励嘴硬补偿',\n avoid: '(鼓励类)人物吃瘪后没有维持体面的反应。',\n fix: '给角色补一句嘴硬找补或自我合理化,保持人设温度。',\n detectPatterns: [],\n builtin: true,\n },\n]\n\n/** 内置题材基底库(常用网文题材树,跨书复用)。 */\nexport const BUILTIN_GENRE_LIBRARY: GenreNode[] = [\n {\n name: '仙侠修真',\n description: '以修仙境界、宗门斗争、法宝丹药为核心,读者期待从凡人到强者的成长与长生问道。',\n children: [\n { name: '凡人流', description: '资质平凡、步步为营,靠资源积累与心机博弈逆袭,强调真实感与代入感。', children: [] },\n { name: '苟道流', description: '主角苟且发育、藏锋敛芒,坐收渔利,强调生存智慧与反差爽点。', children: [] },\n { name: '争霸流', description: '宗门、王朝或大陆争锋,主角由弱到强整合势力,强调格局与权谋。', children: [] },\n ],\n },\n {\n name: '都市异能',\n description: '现代都市背景叠加超能力,读者期待隐藏身份、扮猪吃虎与日常反差。',\n children: [\n { name: '异能升级', description: '觉醒超能力后不断变强,隐藏于都市,遇敌碾压。', children: [] },\n { name: '重生复仇', description: '重生回到过去,利用先知先觉改变命运、清算仇敌。', children: [] },\n { name: '商业经营', description: '以超能力或见识经商扩张,建立商业帝国,强调经营爽感。', children: [] },\n ],\n },\n {\n name: '悬疑推理',\n description: '以谜题、案件与真相揭露为核心,读者期待线索层层展开与反转。',\n children: [\n { name: '本格推理', description: '公平线索、逻辑推演,读者可与主角一同解谜。', children: [] },\n { name: '刑侦探案', description: '警察或侦探视角连续破案,案件串联主线,强调现实与人性。', children: [] },\n { name: '无限流', description: '主角穿梭于不同副本世界解谜求生,副本之间累积成长。', children: [] },\n ],\n },\n {\n name: '玄幻奇幻',\n description: '异世界或架空大陆的冒险成长,读者期待宏大世界观、奇遇与战力突破。',\n children: [\n { name: '学院流', description: '入学修炼、同窗竞争、大赛扬名,强调青春感与阶梯式打脸。', children: [] },\n { name: '废柴逆袭', description: '开局废柴受辱,觉醒金手指后一路逆袭打脸,强调反差与爽点。', children: [] },\n { name: '诸天万界', description: '穿越诸天世界收集资源与能力,强调世界多样性与成长曲线。', children: [] },\n ],\n },\n {\n name: '历史军事',\n description: '以历史时代为背景的争霸、谋略或军旅故事,读者期待权谋博弈与时代质感。',\n children: [\n { name: '王朝争霸', description: '乱世崛起、招贤纳士、逐鹿天下,强调战略与人心。', children: [] },\n { name: '穿越种田', description: '穿越古代发展生产、经营家族,强调建设感与生活细节。', children: [] },\n ],\n },\n {\n name: '末世科幻',\n description: '末世危机或科幻设定下的生存与重建,读者期待资源管理、危机升级与人性考验。',\n children: [\n { name: '基地经营', description: '建立基地、收集资源、抵御危机,强调建设与扩张。', children: [] },\n { name: '进化觉醒', description: '末世异变中觉醒能力不断进化,强调战力成长与危机求生。', children: [] },\n ],\n },\n]\n\n/** 内置常用推进模式。 */\nexport const BUILTIN_PROGRESSION_MODES: ProgressionMode[] = [\n {\n name: '升级变强',\n driver: '主角的实力、境界或能力持续增长,读者期待每次突破带来的碾压与认可。',\n readerExpectation: '每隔几章有一次明确的实力提升或打脸兑现;大境界突破要有仪式感。',\n payoffs: ['突破境界', '学会新技能', '越级战胜强敌', '当众打脸质疑者'],\n risks: ['升级重复套路', '战力膨胀失控', '无铺垫强行突破'],\n primary: false,\n },\n {\n name: '经营扩张',\n driver: '主角的产业、势力或领地不断扩张,资源复利滚雪球。',\n readerExpectation: '经营投入有可感知的回报,扩张遇到新挑战并解决。',\n payoffs: ['新产业上线', '规模翻倍', '吞并对手', '资源闭环成型'],\n risks: ['过程枯燥', '扩张无阻力', '数值失衡'],\n primary: false,\n },\n {\n name: '解谜揭露',\n driver: '主线谜团(身世、阴谋、世界观真相)持续牵引读者,每揭开一层又引出更深一层。',\n readerExpectation: '定期有真相碎片放出,回收旧伏笔、埋设新伏笔。',\n payoffs: ['伏笔回收', '身份揭露', '阴谋浮出水面', '反转打脸'],\n risks: ['谜题拖太久', '伏笔忘记回收', '反转生硬'],\n primary: false,\n },\n {\n name: '渔翁得利',\n driver: '强敌相互厮杀,主角躲在暗处观察、收割,风险由他人承担、果实由主角获取。',\n readerExpectation: '冲突升级时主角以最小代价获取最大收益,且不暴露自身。',\n payoffs: ['坐收渔利', '捡漏宝物', '敌人两败俱伤', '信息差获利'],\n risks: ['重复套路', '收割太轻易', '主角全程无风险'],\n primary: false,\n },\n {\n name: '关系拉扯',\n driver: '人物关系(知己、对手、师徒、情感线)的张力与变化持续推动剧情。',\n readerExpectation: '关系有进有退、有误会与和解,情绪起伏带动阅读欲。',\n payoffs: ['关系升温', '信任建立', '背叛与挽回', '并肩作战'],\n risks: ['情感线停滞', '工业糖精', '为虐而虐'],\n primary: false,\n },\n]\n\n// ------------------------------------------------------------- persistence\n\n/** 默认(空)项目写作资产。 */\nexport function emptyProjectAssets(): ProjectAssets {\n return {\n auxiliaryProgressions: [],\n antiAiRules: [],\n styleAssets: [],\n }\n}\n\n/** 合并项目资产与内置库:返回「生效的反 AI 规则」(内置全局 + 项目自定义)。 */\nexport function effectiveAntiAiRules(assets: ProjectAssets | undefined): AntiAiRule[] {\n const custom = assets?.antiAiRules ?? []\n const customNames = new Set(custom.map(r => r.name))\n return [...BUILTIN_ANTI_AI_RULES.filter(r => !customNames.has(r.name)), ...custom]\n}\n\n/** 把生效规则渲染成提示词块(压缩:avoid/fix 截断,省 token)。 */\nexport function renderAntiAiRules(assets: ProjectAssets | undefined): string {\n const rules = effectiveAntiAiRules(assets)\n if (rules.length === 0) return ''\n const clip = (value: string, max: number): string => value.length > max ? value.slice(0, max) + '…' : value\n return [\n '==================== 反 AI 规则(写作时必须遵守的表达边界) ====================',\n ...rules.map(r => `- ${r.name}:避免——${clip(r.avoid, 90)}${r.fix !== '' ? `;修正——${clip(r.fix, 50)}` : ''}`),\n ].join('\\n')\n}\n\n/** 渲染题材与推进模式提示词块。 */\nexport function renderGenreAndProgression(assets: ProjectAssets | undefined): string {\n const sections: string[] = []\n if (assets?.genre !== undefined) {\n sections.push('==================== 题材基底(本书的题材定位与读者期待) ====================')\n sections.push(`题材:${assets.genre.name}`)\n if (assets.genre.description !== '') sections.push(`读者期待:${assets.genre.description}`)\n const walk = (node: GenreNode, depth: number): void => {\n for (const child of node.children) {\n sections.push(`${' '.repeat(depth)}- ${child.name}:${child.description}`)\n walk(child, depth + 1)\n }\n }\n walk(assets.genre, 1)\n }\n const modes = [\n ...(assets?.primaryProgression !== undefined ? [assets.primaryProgression] : []),\n ...(assets?.auxiliaryProgressions ?? []),\n ]\n if (modes.length > 0) {\n sections.push('==================== 推进模式(读者为什么继续看) ====================')\n for (const mode of modes) {\n const tag = mode.primary ? '(主推进)' : '(辅助)'\n sections.push(`- 模式「${mode.name}」${tag}:驱动力——${mode.driver}`)\n sections.push(` 读者期待:${mode.readerExpectation}`)\n if (mode.payoffs.length > 0) sections.push(` 常见兑现:${mode.payoffs.join('、')}`)\n if (mode.risks.length > 0) sections.push(` 节奏风险(避免):${mode.risks.join('、')}`)\n }\n }\n return sections.join('\\n')\n}\n\n/** 渲染写法资产提示词块(规则去重,省 token)。 */\nexport function renderStyleAssets(assets: ProjectAssets | undefined): string {\n const styles = assets?.styleAssets ?? []\n if (styles.length === 0) return ''\n const sections: string[] = ['==================== 写法资产(本书的叙事风格约束) ====================']\n for (const style of styles) {\n sections.push(`【${style.name}】`)\n const unique = (rules: string[]): string[] => [...new Set(rules)]\n if (style.proseRules.length > 0) sections.push('叙述与节奏:\\n' + unique(style.proseRules).map(r => `- ${r}`).join('\\n'))\n if (style.dialogueRules.length > 0) sections.push('台词风格:\\n' + unique(style.dialogueRules).map(r => `- ${r}`).join('\\n'))\n if (style.descriptionRules.length > 0) sections.push('描写与情绪:\\n' + unique(style.descriptionRules).map(r => `- ${r}`).join('\\n'))\n if (style.boundaries.length > 0) sections.push('表达边界:\\n' + unique(style.boundaries).map(r => `- ${r}`).join('\\n'))\n }\n return sections.join('\\n')\n}\n\n/** 渲染全部写作资产提示词(供生成/规划/审稿注入)。 */\nexport function renderAllAssets(assets: ProjectAssets | undefined): string {\n const parts = [\n renderGenreAndProgression(assets),\n renderStyleAssets(assets),\n renderAntiAiRules(assets),\n ].filter(part => part !== '')\n return parts.join('\\n\\n')\n}\n\n/** 预置写法模板 → 可直接绑定的 StyleAsset。 */\nexport function styleTemplateToAsset(template: StyleTemplate): StyleAsset {\n return {\n name: template.name,\n proseRules: [...template.proseRules, ...template.rhythmRules.map(r => `节奏:${r}`)],\n dialogueRules: template.dialogueRules,\n descriptionRules: template.languageRules,\n boundaries: [`模板「${template.name}」适用题材:${template.applicableGenres.join('、')}`, '不要违背模板的叙事单元结构与节奏约束'],\n createdAt: new Date().toISOString(),\n }\n}\n\n// ------------------------------------------------------- style extraction\n\n/** 写法引擎:从样本文本提取风格资产的系统提示词。 */\nexport function styleEngineSystemPrompt(): string {\n return [\n '你是一位资深网文文风分析师。你会收到一段样本文本,请提炼出可复用的叙事风格规则,供后续章节保持同一种味道。',\n '要求:',\n '1. 从样本中归纳,不要泛泛而谈;每条规则都要能落到具体写法(句式、用词、视角、节奏、对话方式、描写密度)。',\n '2. 台词风格要说明角色说话的语气特征与常用表达方式。',\n '3. 表达边界要写明这段风格「不会怎么做」(如:不用华丽辞藻、不写长段心理独白、不用成语堆砌)。',\n '4. 输出必须是合法 JSON 对象,不要输出任何其他文字。',\n 'JSON 结构:',\n '{\"proseRules\": [\"叙述视角与句式节奏规则\"], \"dialogueRules\": [\"台词风格规则\"], \"descriptionRules\": [\"描写密度与情绪表达规则\"], \"boundaries\": [\"表达边界\"]}',\n ].join('\\n')\n}\n","/**\n * Novel engine — the host half's core: LLM-driven story-bible extraction,\n * volume planning, chapter planning, chapter-by-chapter writing with\n * auto-review + rewrite, polish (de-AI-ify), narrative summaries, foreshadow\n * tracking, project persistence, and whole-book export. Pure Node (no\n * web-server dependencies), so routes stay thin and logic is testable.\n */\n\nimport { mkdirSync, readFileSync, writeFileSync, readdirSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createUserMessage, BlockAssembler, ReasoningEffortId, type GenerateOptions, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { emptyProjectAssets, renderAllAssets, styleEngineSystemPrompt } from './assets.ts'\nimport type {\n AuditIssue,\n ChapterPlan,\n Foreshadow,\n NovelConfig,\n ProjectState,\n ReviewReport,\n RoleStatusCard,\n StoryBible,\n Volume,\n WorldState,\n} from './protocol.ts'\n\n/** Project state file name inside the output dir. */\nexport const PROJECT_FILE = 'novel-project.json'\n\n// ------------------------------------------------------------------ helpers\n\n/** Sanitize a file name: keep CJK/alphanumerics/space/dash/underscore. */\nfunction safeFileName(name: string): string {\n return name\n .replace(/[\\\\/:*?\"<>|]/g, '')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, 60)\n}\n\n/** Chapter output file name, e.g. 第001章_开篇.md */\nexport function chapterFileName(chapter: ChapterPlan): string {\n const title = safeFileName(chapter.title) || `第${chapter.no}章`\n return `第${String(chapter.no).padStart(3, '0')}章_${title}.md`\n}\n\n/** Infer a book name from the outline's first non-empty line. */\nexport function inferBookName(outline: string): string {\n const line = outline.split('\\n').map(l => l.trim()).find(l => l.length > 0)\n return (line ?? '未命名小说').replace(/^《/, '').replace(/》.*$/, '').slice(0, 40)\n}\n\n// ------------------------------------------------------------------ project\n\n/** Read the persisted project from the output dir (undefined when absent). */\nexport function loadProject(outputDir: string): ProjectState | undefined {\n const file = join(outputDir, PROJECT_FILE)\n if (!existsSync(file)) return undefined\n try {\n let rawText = readFileSync(file, 'utf8')\n // Tolerate a UTF-8 BOM (some editors / PowerShell writes add one).\n if (rawText.charCodeAt(0) === 0xFEFF) rawText = rawText.slice(1)\n const raw = JSON.parse(rawText) as ProjectState\n if (typeof raw.outline !== 'string' || !Array.isArray(raw.chapters)) return undefined\n // Normalize legacy projects (foreshadows / assets may be missing).\n if (!Array.isArray(raw.foreshadows)) raw.foreshadows = []\n if (raw.assets === undefined || typeof raw.assets !== 'object') raw.assets = emptyProjectAssets()\n if (!Array.isArray(raw.assets.antiAiRules)) raw.assets.antiAiRules = []\n if (!Array.isArray(raw.assets.auxiliaryProgressions)) raw.assets.auxiliaryProgressions = []\n if (!Array.isArray(raw.assets.styleAssets)) raw.assets.styleAssets = []\n if (!Array.isArray(raw.facts)) raw.facts = []\n return raw\n } catch {\n return undefined\n }\n}\n\n/** Persist the project state next to the chapters. */\nexport function saveProject(outputDir: string, project: ProjectState): void {\n mkdirSync(outputDir, { recursive: true })\n writeFileSync(join(outputDir, PROJECT_FILE), JSON.stringify(project, null, 2), 'utf8')\n}\n\n/** List generated chapter files in the output dir (sorted). */\nexport function listChapterFiles(outputDir: string): string[] {\n if (!existsSync(outputDir)) return []\n try {\n return readdirSync(outputDir)\n .filter(name => /^第\\d+章_.*\\.md$/.test(name) && !name.endsWith('.bak.md'))\n .sort((a, b) => {\n const na = Number(/^第(\\d+)章/.exec(a)?.[1] ?? 0)\n const nb = Number(/^第(\\d+)章/.exec(b)?.[1] ?? 0)\n return na - nb\n })\n } catch {\n return []\n }\n}\n\n/** Re-sync chapter status against files on disk (a file may exist without state). */\nexport function syncProjectWithDisk(project: ProjectState, outputDir: string): void {\n const files = new Map<string, string>()\n for (const file of listChapterFiles(outputDir)) {\n const no = Number(/^第(\\d+)章/.exec(file)?.[1] ?? 0)\n if (no > 0) files.set(String(no), file)\n }\n for (const chapter of project.chapters) {\n const file = files.get(String(chapter.no))\n if (file !== undefined && (chapter.status === 'pending' || chapter.status === 'generating')) {\n chapter.status = 'written'\n chapter.file = file\n }\n }\n project.updatedAt = new Date().toISOString()\n}\n\n/** Read a chapter's markdown body from disk (undefined when missing). */\nexport function readChapterFile(outputDir: string, chapter: ChapterPlan): string | undefined {\n if (chapter.file === undefined) return undefined\n const path = join(outputDir, chapter.file)\n if (!existsSync(path)) return undefined\n return readFileSync(path, 'utf8')\n}\n\n/** Create a fresh project from an outline. */\nexport function createProject(outline: string, outlinePath?: string): ProjectState {\n const now = new Date().toISOString()\n return {\n bookName: inferBookName(outline),\n outline,\n outlinePath,\n chapters: [],\n foreshadows: [],\n assets: emptyProjectAssets(),\n facts: [],\n createdAt: now,\n updatedAt: now,\n }\n}\n\n// ------------------------------------------------------------------- llm\n\n/** One complete non-streaming LLM call. */\nasync function complete(\n ctx: Context,\n config: NovelConfig,\n options: { system: string; user: string; temperature?: number; maxTokens?: number },\n): Promise<string> {\n const messages: Message[] = [createUserMessage({\n content: [{ type: 'text', text: options.user }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n })]\n const request: GenerateOptions = {\n provider: config.provider,\n model: config.model,\n messages,\n system: options.system,\n maxTokens: options.maxTokens ?? config.maxTokens,\n temperature: options.temperature ?? 0.7,\n }\n const assembler = new BlockAssembler()\n for await (const chunk of ctx.llm.stream(request)) {\n assembler.push(chunk)\n }\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n throw new Error(`LLM 调用失败(${finish.kind}): ${finish.failure.message}`)\n }\n if (finish.kind === 'max-tokens') {\n throw new Error('LLM 输出达到 maxTokens 上限,请增大配置后重试')\n }\n const blocks = assembler.blocks()\n // Diagnostics: log the assembled block shape (reasoning-only turns yield no\n // text blocks — the v4-flash model can answer entirely in the reasoning\n // channel, which the adapter surfaces as a reasoning block).\n if (process.env.DSH_NOVEL_DEBUG === '1') {\n console.error('[dsh-novel-forge] complete: finish=%j blocks=%j', JSON.stringify(finish), blocks.map(b => `${b.type}:${'text' in b ? b.text.length : '?'}`))\n }\n const textBlocks = blocks\n .filter((block): block is Extract<StreamChunk, { type: 'block-end' }>['block'] & { type: 'text' } => block.type === 'text')\n .map(block => block.text)\n let text = textBlocks.join('\\n').trim()\n // v4-flash can answer entirely in the reasoning channel (the adapter\n // surfaces that as a 'reasoning' block). Fall back to it when no text came\n // back — the reasoning content is the model's actual answer here.\n if (text === '') {\n const reasoning = blocks\n .filter((block): block is { type: 'reasoning'; text: string } => block.type === 'reasoning')\n .map(block => block.text)\n .join('\\n')\n .trim()\n if (reasoning !== '') text = reasoning\n }\n return text\n}\n\n/**\n * Parse a JSON value out of a model response. Multi-level tolerance because\n * models are sloppy: prose around the JSON, ```json fences, a truncated tail,\n * or raw newlines inside string values all defeat a single JSON.parse. We\n * walk candidates from strictest to loosest.\n */\nfunction parseJson<T>(text: string, wantArray: boolean): T {\n const candidates: string[] = []\n const push = (value: string | undefined): void => {\n if (value !== undefined && value.trim() !== '') candidates.push(value.trim())\n }\n\n // 1. Whole response, and any ```json fence body.\n push(text)\n const fenced = /```(?:json)?\\s*([\\s\\S]*?)```/.exec(text)\n push(fenced?.[1])\n // 2. From the first opener to the last closer.\n const opener = wantArray ? '[' : '{'\n const closer = wantArray ? ']' : '}'\n const start = text.indexOf(opener)\n const end = text.lastIndexOf(closer)\n if (start !== -1 && end > start) push(text.slice(start, end + 1))\n // 3. Trim trailing prose (a \"}...\" tail after the last closer).\n const trimmed = text.replace(new RegExp(`${closer}[\\\\s\\\\S]*$`), closer)\n push(trimmed)\n const start2 = trimmed.indexOf(opener)\n if (start2 !== -1) push(trimmed.slice(start2))\n\n // Repair: models love raw newlines inside string values, which JSON forbids.\n const repair = (value: string): string => {\n let out = ''\n let inString = false\n for (let i = 0; i < value.length; i++) {\n const ch = value[i]!\n if (inString) {\n if (ch === '\\\\') {\n out += ch + (value[i + 1] ?? '')\n i++\n continue\n }\n if (ch === '\"') {\n inString = false\n out += ch\n continue\n }\n if (ch === '\\n' || ch === '\\r') {\n out += '\\\\n'\n continue\n }\n out += ch\n } else {\n if (ch === '\"') inString = true\n out += ch\n }\n }\n return out\n }\n\n for (const candidate of candidates) {\n for (const attempt of [candidate, repair(candidate)]) {\n try {\n return JSON.parse(attempt) as T\n } catch {\n // try the next candidate\n }\n }\n }\n const preview = text.length > 300 ? text.slice(0, 300) + '…' : text\n throw new Error(`模型输出中未找到 JSON 数据。模型原始输出:${preview}`)\n}\n\n/** Parse a JSON array (chapters, volumes, issues...). */\nfunction parseJsonArray<T>(text: string): T[] {\n const value = parseJson<T[]>(text, true)\n return Array.isArray(value) ? value : []\n}\n\n/** Parse a JSON object. */\nfunction parseJsonObject<T>(text: string): T {\n const value = parseJson<T>(text, false)\n if (typeof value !== 'object' || value === null) throw new Error('模型输出不是 JSON 对象')\n return value\n}\n\n// ------------------------------------------------------------------ bible\n\n/** System prompt for story-bible extraction. */\nfunction bibleSystemPrompt(): string {\n return [\n '你是一位资深网文编辑兼设定架构师。你会收到一份小说大纲,请把它提炼成结构化的「设定圣经」(Story Bible),供后续写作时严格引用。',\n '要求:',\n '1. 忠于大纲,不自行发明大纲之外的设定。',\n '2. 角色卡覆盖大纲明确出现的角色(主角必含),每个角色给出性格标签、目标、关键关系。',\n '3. 世界规则覆盖力量体系、金手指机制、势力、地理等所有硬性规则,逐条列出。',\n '4. 红线列出大纲中明确禁止的内容(如无后宫、不圣母、无无脑碾压等)。',\n '5. 风格列出叙事基调、节奏、POV 等写作风格要点。',\n '输出必须是合法 JSON 对象,不要输出任何其他文字或 Markdown 代码块标记。',\n '重要:所有字符串值内部不得包含换行符(不要用多行字符串),JSON 必须在一段内完整结束。',\n '重要:直接输出 JSON 结果本身,不要把思考过程或推理内容写在输出里。',\n 'JSON 结构:',\n '{\"genre\": \"题材与基调一句话\", \"worldRules\": [\"规则1\", \"规则2\", ...], \"characters\": [{\"name\": \"角色名\", \"role\": \"protagonist|supporting|antagonist|other\", \"traits\": [\"标签1\", ...], \"goals\": \"目标与动机\", \"relations\": \"关键关系\"}], \"redLines\": [\"红线1\", ...], \"style\": [\"风格1\", ...]}',\n ].join('\\n')\n}\n\n/** Extract the story bible from an outline. */\nexport async function extractBible(ctx: Context, config: NovelConfig, outline: string): Promise<StoryBible> {\n const user = `请为下面这部小说提炼设定圣经:\\n\\n${outline}`\n const text = await complete(ctx, config, {\n system: bibleSystemPrompt(),\n user,\n temperature: 0.4,\n maxTokens: Math.max(config.maxTokens, 16000),\n })\n const raw = parseJsonObject<{\n genre?: unknown\n worldRules?: unknown\n characters?: unknown\n redLines?: unknown\n style?: unknown\n }>(text)\n const strArray = (value: unknown): string[] =>\n Array.isArray(value) ? value.filter((v): v is string => typeof v === 'string' && v.trim() !== '') : []\n const characters: StoryBible['characters'] = Array.isArray(raw.characters)\n ? raw.characters\n .filter((v): v is Record<string, unknown> => typeof v === 'object' && v !== null)\n .map(entry => ({\n name: typeof entry.name === 'string' ? entry.name.trim() : '未命名',\n role: (['protagonist', 'supporting', 'antagonist', 'other'] as const).includes(entry.role as never)\n ? entry.role as StoryBible['characters'][number]['role']\n : 'other',\n traits: strArray(entry.traits),\n goals: typeof entry.goals === 'string' ? entry.goals : '',\n relations: typeof entry.relations === 'string' ? entry.relations : '',\n }))\n .filter(card => card.name !== '')\n : []\n const bible: StoryBible = {\n genre: typeof raw.genre === 'string' ? raw.genre : '',\n worldRules: strArray(raw.worldRules),\n characters,\n redLines: strArray(raw.redLines),\n style: strArray(raw.style),\n generatedAt: new Date().toISOString(),\n }\n if (bible.worldRules.length === 0 && bible.characters.length === 0 && bible.redLines.length === 0) {\n throw new Error('道藏生成失败:模型没有返回有效内容')\n }\n return bible\n}\n\n// ------------------------------------------------------------------ volumes\n\n/** System prompt for volume planning. */\nfunction volumeSystemPrompt(): string {\n return [\n '你是一位资深网文总编。你会收到一份小说大纲,请把全书划分为若干「卷」(分卷),每卷有明确的剧情定位与起止章节。',\n '要求:',\n '1. 大纲已有分卷时,严格遵循大纲的分卷结构;没有时按剧情弧线合理划分(3-8 卷)。',\n '2. 卷定位一句话说明该卷的剧情重心。',\n '3. chapterStart/chapterEnd 给出该卷覆盖的章节区间(从 1 开始连续编号)。',\n '输出必须是合法 JSON 数组,不要输出任何其他文字:',\n '[{\"no\": 1, \"title\": \"卷名\", \"summary\": \"卷定位与剧情重心\", \"chapterStart\": 1, \"chapterEnd\": 80}]',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n '重要:直接输出 JSON 结果本身,不要把思考过程或推理内容写在输出里。',\n ].join('\\n')\n}\n\n/** Plan volumes from an outline. */\nexport async function planVolumes(ctx: Context, config: NovelConfig, outline: string): Promise<Volume[]> {\n const user = `请为下面这部小说划分卷:\\n\\n${outline}`\n const text = await complete(ctx, config, { system: volumeSystemPrompt(), user, temperature: 0.4, maxTokens: Math.max(config.maxTokens, 12000) })\n const parsed = parseJsonArray<Record<string, unknown>>(text)\n const volumes: Volume[] = []\n for (let i = 0; i < parsed.length; i++) {\n const entry = parsed[i]\n if (typeof entry !== 'object' || entry === null) continue\n const no = typeof entry.no === 'number' ? entry.no : i + 1\n const title = typeof entry.title === 'string' ? entry.title.trim() : `第${no}卷`\n const summary = typeof entry.summary === 'string' ? entry.summary.trim() : ''\n const start = typeof entry.chapterStart === 'number' ? entry.chapterStart : undefined\n const end = typeof entry.chapterEnd === 'number' ? entry.chapterEnd : undefined\n volumes.push({\n no,\n title: title.slice(0, 40),\n summary: summary.slice(0, 300),\n chapterStart: start ?? 1,\n chapterEnd: end ?? 1,\n })\n }\n if (volumes.length === 0) throw new Error('卷计划生成失败:模型没有返回有效卷')\n return volumes\n}\n\n/** Assign a chapter to its volume by number. */\nfunction volumeOf(chapterNo: number, volumes: Volume[] | undefined): number {\n if (volumes === undefined || volumes.length === 0) return 0\n for (const volume of volumes) {\n if (chapterNo >= volume.chapterStart && chapterNo <= volume.chapterEnd) return volume.no\n }\n return volumes[volumes.length - 1]?.no ?? 0\n}\n\n// ------------------------------------------------------------------- plan\n\n/** The chapter-planning prompt template. */\nfunction planSystemPrompt(volumes: Volume[] | undefined): string {\n const volumeBlock = volumes !== undefined && volumes.length > 0\n ? ['\\n全书分卷结构(规划章节时需落在对应卷内):']\n .concat(volumes.map(v => `第${v.no}卷《${v.title}》:${v.summary}(章节 ${v.chapterStart}-${v.chapterEnd})`))\n .join('\\n')\n : ''\n return [\n '你是一位资深中文网文策划编辑,擅长把小说大纲拆解为可执行的章节计划。',\n '你会收到一份小说大纲。请根据大纲的设定、主线与节奏,规划出一份章节计划。',\n '要求:',\n '1. 每章必须有明确的核心剧情推进(不能只是过渡或凑字数)。',\n '2. 章节之间要衔接自然,前章结尾为后章埋下钩子。',\n '3. 严格遵循大纲的人设、金手指规则、战力体系与世界观设定,不得自行发明冲突设定。',\n '4. 输出必须是合法的 JSON 数组,不要输出任何其他文字或 Markdown 代码块标记。',\n '5. 数组每个元素格式:{\"title\": \"章节标题(10字以内,有网文感)\", \"beats\": \"结构化剧情要点(150-250字,必须包含四段,段间用换行分隔):\\\\n本章目标:本章要完成的核心推进;\\\\n剧情要点:主要情节的起承转合(2-4 句);\\\\n爽点/钩子:本章的爽点兑现或情绪钩子;\\\\n结尾钩子:本章结尾为下一章埋下的悬念\"}',\n '重要:beats 字段内部必须使用 \\\\n 转义表示换行(JSON 字符串内不得有真实换行符),其余字符串值也不得包含真实换行符,JSON 必须在一段内完整结束。',\n '重要:直接输出 JSON 结果本身,不要把思考过程或推理内容写在输出里。',\n volumeBlock,\n ].join('\\n')\n}\n\n/** Build the writing system prompt (bible + outline + active foreshadows). */\nfunction writeSystemPrompt(project: ProjectState): string {\n const bible = project.bible\n const sections: string[] = []\n if (bible !== undefined) {\n sections.push('==================== 设定圣经(写作时严格遵守) ====================')\n if (bible.genre !== '') sections.push(`题材基调:${bible.genre}`)\n if (bible.worldRules.length > 0) sections.push('世界规则:\\n' + bible.worldRules.map(r => `- ${r}`).join('\\n'))\n if (bible.characters.length > 0) {\n sections.push('角色卡:')\n for (const card of bible.characters) {\n const roleName = { protagonist: '主角', supporting: '配角', antagonist: '反派', other: '其他' }[card.role]\n sections.push(`- ${card.name}(${roleName}):${card.traits.join('、')}${card.goals !== '' ? `;目标:${card.goals}` : ''}${card.relations !== '' ? `;关系:${card.relations}` : ''}`)\n }\n }\n if (bible.redLines.length > 0) sections.push('写作红线(违反即失败):\\n' + bible.redLines.map(r => `- ${r}`).join('\\n'))\n if (bible.style.length > 0) sections.push('风格要求:\\n' + bible.style.map(r => `- ${r}`).join('\\n'))\n }\n const worldBlock = renderWorld(project.world)\n if (worldBlock !== '') sections.push(worldBlock)\n sections.push('==================== 全书大纲 ====================')\n // 超长大纲截断保护(防止上下文超限);完整大纲在总纲页查看。\n const outlineBlock = project.outline.length > 6000\n ? project.outline.slice(0, 6000) + '\\n…(大纲过长已节选,完整内容见总纲页)'\n : project.outline\n sections.push(outlineBlock)\n sections.push('==================== 大纲结束 ====================')\n const assetsBlock = renderAllAssets(project.assets)\n if (assetsBlock !== '') sections.push(assetsBlock)\n const active = project.foreshadows.filter(f => f.status === 'planted' || f.status === 'progressing')\n if (active.length > 0) {\n sections.push('==================== 活跃伏笔(近期需推进或回收的线索) ====================')\n for (const f of active) {\n sections.push(`- [${f.status === 'planted' ? '已埋设' : '推进中'}] ${f.description}${f.targetChapter !== undefined ? `(预计 ${f.targetChapter} 章回收)` : ''}`)\n }\n }\n sections.push('')\n sections.push('写作硬性要求:')\n sections.push('1. 每章 3000-4000 字(按中文字符计),只输出章节正文,不要输出标题、章回名、作者的话或任何 Markdown 标记。')\n sections.push('2. 以主角视角展开,动作、对话、心理描写交替推进,禁止大段设定说明。')\n sections.push('3. 尊重大纲与设定圣经:人设不崩、金手指规则不自相矛盾、战力不随意膨胀。')\n sections.push('4. 章末留一个钩子(悬念、反转或新线索),吸引读者读下一章。')\n sections.push('5. 语言流畅自然,符合中文网文语感,避免翻译腔与病句。')\n return sections.join('\\n')\n}\n\n/**\n * Plan chapters from an outline (optionally for one volume).\n */\nexport async function planChapters(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n chapterCount: number,\n volumeNo?: number,\n): Promise<ChapterPlan[]> {\n const volume = project.volumes?.find(v => v.no === volumeNo)\n const user = [\n '请为下面这部小说规划章节。',\n volume !== undefined\n ? `本次只规划第 ${volume.no} 卷《${volume.title}》的章节:\\n${volume.summary}`\n : '请规划全书开篇章节。',\n `大纲如下:\\n${project.outline}`,\n '',\n `请规划 ${chapterCount} 章。输出 JSON 数组(不要输出其他文字):`,\n ].join('\\n')\n const text = await complete(ctx, config, { system: planSystemPrompt(project.volumes), user, temperature: 0.7, maxTokens: Math.max(config.maxTokens, 20000) })\n const parsed = parseJsonArray<Record<string, unknown>>(text)\n const chapters: ChapterPlan[] = []\n const existing = new Set(project.chapters.map(c => c.no))\n const startNo = project.chapters.length + 1\n for (let i = 0; i < Math.min(parsed.length, chapterCount); i++) {\n const item = parsed[i]\n if (typeof item !== 'object' || item === null) continue\n const entry = item as Record<string, unknown>\n const title = typeof entry.title === 'string' ? entry.title.trim().slice(0, 30) : ''\n const beats = typeof entry.beats === 'string' ? entry.beats.trim() : ''\n if (title === '' && beats === '') continue\n const no = startNo + i\n if (existing.has(no)) continue\n chapters.push({\n no,\n volume: volumeOf(no, project.volumes),\n title: title || `第${no}章`,\n beats,\n targetChars: config.chapterChars,\n status: 'pending',\n })\n }\n if (chapters.length === 0) {\n throw new Error('章节计划生成失败:模型没有返回有效章节')\n }\n return chapters\n}\n\n// ------------------------------------------------------------------ writing\n\n/** The review system prompt. */\nfunction reviewSystemPrompt(project: ProjectState): string {\n const bible = project.bible\n const sections: string[] = [\n '你是一位严格的网文审稿编辑。你会收到一章正文以及本书的设定圣经与红线。',\n '请从以下维度审查本章:',\n '1. 人设一致性:角色行为是否符合角色卡(主角不圣母、不无脑、痞坏有分寸等)。',\n '2. 设定一致性:金手指规则、战力体系、世界观是否与设定圣经冲突。',\n '3. 红线检查:是否触犯写作红线(无后宫、无擦边、无无脑碾压等)。',\n '4. 文笔质量:语病、翻译腔、AI 套话(\"不禁\"\"仿佛\"\"一时间\"等高频词滥用)、流水账。',\n '5. 节奏与爽点:本章是否有推进、有钩子,是否拖沓灌水。',\n '6. 逻辑漏洞:前后矛盾、时间线错误、对话失真。',\n '7. 反 AI 规则:逐条核对下方「反 AI 规则」清单,命中即列为问题。',\n '输出必须是合法 JSON 对象,不要输出任何其他文字:',\n '{\"score\": 0-100的整数, \"verdict\": \"一句话总评\", \"issues\": [{\"severity\": \"high|medium|low\", \"item\": \"问题描述\", \"suggestion\": \"修改建议\"}]}',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n '重要:直接输出 JSON 结果本身,不要把思考过程写在输出里。',\n ]\n const assetsBlock = renderAllAssets(project.assets)\n if (assetsBlock !== '') sections.push('\\n' + assetsBlock)\n if (bible !== undefined) {\n sections.push('\\n==================== 设定圣经 ====================')\n if (bible.worldRules.length > 0) sections.push('世界规则:\\n' + bible.worldRules.map(r => `- ${r}`).join('\\n'))\n if (bible.characters.length > 0) {\n sections.push('角色卡:')\n for (const card of bible.characters) {\n sections.push(`- ${card.name}(${card.role}):${card.traits.join('、')}`)\n }\n }\n if (bible.redLines.length > 0) sections.push('红线:\\n' + bible.redLines.map(r => `- ${r}`).join('\\n'))\n }\n return sections.join('\\n')\n}\n\n/** Run the AI review on one chapter. */\nexport async function reviewChapter(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): Promise<ReviewReport> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) throw new Error(`章节 ${chapterNo} 不在计划中`)\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) throw new Error(`章节 ${chapterNo} 的正文文件不存在`)\n const user = [\n `本章标题:《${chapter.title}》`,\n `本章剧情要点:${chapter.beats}`,\n '==================== 章节正文 ====================',\n body.replace(/^#\\s+.*$/m, '').trim(),\n ].join('\\n')\n const text = await complete(ctx, config, { system: reviewSystemPrompt(project), user, temperature: 0.3, maxTokens: Math.max(config.maxTokens, 16000) })\n const raw = parseJsonObject<{ score?: unknown; verdict?: unknown; issues?: unknown }>(text)\n const issues = Array.isArray(raw.issues)\n ? raw.issues\n .filter((v): v is Record<string, unknown> => typeof v === 'object' && v !== null)\n .map(entry => ({\n severity: (['high', 'medium', 'low'] as const).includes(entry.severity as never)\n ? entry.severity as 'high' | 'medium' | 'low'\n : 'medium',\n item: typeof entry.item === 'string' ? entry.item : '',\n suggestion: typeof entry.suggestion === 'string' ? entry.suggestion : '',\n }))\n .filter(issue => issue.item !== '')\n : []\n const score = typeof raw.score === 'number' ? Math.max(0, Math.min(100, Math.round(raw.score))) : 60\n const report: ReviewReport = {\n score,\n passed: score >= config.reviewPassScore,\n verdict: typeof raw.verdict === 'string' ? raw.verdict.slice(0, 200) : '',\n issues,\n reviewedAt: new Date().toISOString(),\n }\n chapter.review = report\n chapter.status = report.passed ? 'approved' : 'rejected'\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return report\n}\n\n/**\n * 审查「任意正文文本」(作者手动编辑后的草稿,不落盘)。\n * 复用审稿提示词与红线/道藏/反AI规则;仅返回报告,不改文件不改状态。\n */\nexport async function reviewChapterText(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n text: string,\n): Promise<ReviewReport> {\n const user = [\n `书名:《${project.bookName}》`,\n '==================== 待审查正文 ====================',\n text.slice(0, 20000),\n ].join('\\n')\n const raw = parseJsonObject<{ score?: unknown; verdict?: unknown; issues?: unknown }>(\n await complete(ctx, config, { system: reviewSystemPrompt(project), user, temperature: 0.3, maxTokens: Math.max(config.maxTokens, 8000) }),\n )\n const issues = Array.isArray(raw.issues)\n ? raw.issues\n .filter((v): v is Record<string, unknown> => typeof v === 'object' && v !== null)\n .map(entry => ({\n severity: (['high', 'medium', 'low'] as const).includes(entry.severity as never)\n ? entry.severity as 'high' | 'medium' | 'low'\n : 'medium',\n item: typeof entry.item === 'string' ? entry.item : '',\n suggestion: typeof entry.suggestion === 'string' ? entry.suggestion : '',\n }))\n .filter(issue => issue.item !== '')\n : []\n const score = typeof raw.score === 'number' ? Math.max(0, Math.min(100, Math.round(raw.score))) : 60\n return {\n score,\n passed: score >= config.reviewPassScore,\n verdict: typeof raw.verdict === 'string' ? raw.verdict.slice(0, 200) : '',\n issues,\n reviewedAt: new Date().toISOString(),\n }\n}\n\n/** Build the rewrite system prompt (fix review issues / instructions). */\nfunction rewriteSystemPrompt(project: ProjectState): string {\n const base = writeSystemPrompt(project)\n return base + '\\n\\n额外要求:你正在【修订】一章已写好的正文。保留原文中好的部分,只修改需要修改的地方,输出完整的新正文(不要只输出修改片段),字数与原文相当。'\n}\n\n/**\n * Stream a chapter rewrite. With `target` (a passage of the body), only that\n * passage's paragraph is rewritten and spliced back — everything else stays\n * untouched (local revision). Without `target`, the whole chapter is\n * rewritten. Yields delta text; persists when done.\n */\nexport async function* rewriteChapterStream(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n instructions: string,\n target?: string,\n): AsyncGenerator<{ frame: 'start' } | { frame: 'delta'; text: string } | { frame: 'drafted'; chars: number; draft: string }, void, unknown> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) throw new Error(`章节 ${chapterNo} 不在计划中`)\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) throw new Error(`章节 ${chapterNo} 的正文文件不存在`)\n\n const reviewBlock = chapter.review !== undefined\n ? '审稿意见:\\n' + chapter.review.issues.map(i => `[${i.severity}] ${i.item} → ${i.suggestion}`).join('\\n')\n : ''\n\n // Local revision: find the paragraph containing `target` and only rewrite it.\n const bodyText = body.replace(/^#\\s+.*$/m, '').trim()\n let localTarget: { paragraph: string; before: string; after: string } | undefined\n if (target !== undefined && target.trim() !== '') {\n const wanted = target.trim()\n // Normalize whitespace so multi-line / quoted snippets still match:\n // the assistant often copies a passage with line breaks and quotes.\n const normalize = (value: string): string => value.replace(/\\s+/g, ' ').replace(/[“”\"'‘’]/g, '')\n const wantedFlat = normalize(wanted)\n // Split into paragraphs on blank lines (or double newlines).\n const paragraphs = bodyText.split(/\\n{2,}/)\n const idx = paragraphs.findIndex(p => normalize(p).includes(wantedFlat))\n if (idx === -1) {\n throw new Error(`在正文中未找到要修改的片段:「${wanted.slice(0, 40)}…」。请从正文中复制原文片段(无需整段,取片段即可)。`)\n }\n localTarget = {\n paragraph: paragraphs[idx]!,\n before: paragraphs.slice(0, idx).join('\\n\\n'),\n after: paragraphs.slice(idx + 1).join('\\n\\n'),\n }\n }\n\n const user = localTarget === undefined\n ? [\n `请修订第 ${chapter.no} 章《${chapter.title}》。`,\n reviewBlock,\n instructions !== '' ? `本次修订重点:${instructions}` : '',\n '==================== 原正文 ====================',\n bodyText,\n ].filter(line => line !== '').join('\\n')\n : [\n `请修订第 ${chapter.no} 章《${chapter.title}》中的一个自然段。`,\n instructions !== '' ? `修改要求:${instructions}` : '',\n '==================== 需要修改的原文段落 ====================',\n localTarget.paragraph,\n '',\n '要求:',\n '1. 只输出修改后的【这一个段落】的完整新文本,不要输出任何说明、标题或 Markdown 标记。',\n '2. 保留该段的情节走向与角色口吻,只按修改要求调整。',\n '3. 段落长度与原文相当。',\n ].filter(line => line !== '').join('\\n')\n\n const system = localTarget === undefined\n ? rewriteSystemPrompt(project)\n : '你是一位中文网文润色师。你会收到一章中的一个段落,请按修改要求重写该段。只输出新段落文本。'\n\n const messages: Message[] = [createUserMessage({\n content: [{ type: 'text', text: user }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n })]\n const request: GenerateOptions = {\n provider: config.provider,\n model: config.model,\n messages,\n system,\n // Rewriting outputs a full chapter: budget generously and skip\n // reasoning (a transform task) so the whole budget goes to the body.\n maxTokens: Math.max(config.maxTokens, 20000),\n temperature: 0.7,\n reasoningEffort: ReasoningEffortId('off'),\n }\n\n yield { frame: 'start' }\n const assembler = new BlockAssembler()\n let streamError: Error | undefined\n for await (const chunk of ctx.llm.stream(request)) {\n assembler.push(chunk)\n if (chunk.type === 'text-delta') yield { frame: 'delta', text: chunk.text }\n }\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n streamError = new Error(`修订失败(${finish.kind}): ${finish.failure.message}`)\n } else if (finish.kind === 'max-tokens') {\n streamError = new Error('修订输出达到 maxTokens 上限,请增大配置后重试')\n }\n const rewritten = assembler\n .blocks()\n .filter((block): block is Extract<StreamChunk, { type: 'block-end' }>['block'] & { type: 'text' } => block.type === 'text')\n .map(block => block.text)\n .join('\\n')\n .trim()\n if (streamError !== undefined) throw streamError\n if (rewritten.length < 20) throw new Error('修订结果过短,可能失败,请重试')\n\n // Splice: local -> replace the paragraph; whole -> replace the body.\n let newBody: string\n if (localTarget !== undefined) {\n newBody = [localTarget.before, rewritten, localTarget.after].filter(part => part !== '').join('\\n\\n')\n } else {\n newBody = rewritten\n }\n if (newBody.length < 100) throw new Error('修订结果过短,可能失败,请重试')\n\n // Draft mode: do NOT overwrite the file yet. Store the new body as a\n // pending draft; the user reviews the diff and decides to apply or\n // discard. File overwrite + status change happen on draft/apply.\n chapter.pendingDraft = newBody\n chapter.error = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n yield { frame: 'drafted', chars: newBody.length, draft: newBody }\n}\n\n/** The de-AI-ify polish system prompt (with project writing assets injected). */\nfunction polishSystemPrompt(project: ProjectState): string {\n const assetsBlock = renderAllAssets(project.assets)\n return [\n '你是一位中文网文润色师。你会收到一章正文,请做「去 AI 味」润色:',\n '1. 删除/替换 AI 高频套话与模式词:如\"不禁\"\"仿佛\"\"一时间\"\"不由得\"\"顿时\"\"然而\"\"缓缓\"\"轻轻\"\"微微\"\"默默\"\"似乎\"\"终于\"等滥用。',\n '2. 把书面翻译腔改成口语化的中文网文语感。',\n '3. 拆分过长的排比句与堆砌的修饰语。',\n '4. 保留全部情节、人物、对话内容不变,只改表达。',\n '5. 输出完整的新正文,不要输出任何说明文字或 Markdown 标记。',\n '6. 必须遵守下方「反 AI 规则」与「写法资产」的表达边界;写法资产要求保留的风格特征(句式、台词、节奏)不得在润色中丢失。',\n assetsBlock !== '' ? assetsBlock : '',\n ].join('\\n')\n}\n\n/** Stream a chapter polish (de-AI-ify). Draft-mode: the polished body lands\n * in `chapter.pendingDraft` and is only applied on draft/apply. */\nexport async function* polishChapterStream(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): AsyncGenerator<{ frame: 'start' } | { frame: 'delta'; text: string } | { frame: 'drafted'; chars: number; draft: string }, void, unknown> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) throw new Error(`章节 ${chapterNo} 不在计划中`)\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) throw new Error(`章节 ${chapterNo} 的正文文件不存在`)\n const messages: Message[] = [createUserMessage({\n content: [{ type: 'text', text: body.replace(/^#\\s+.*$/m, '').trim() }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n })]\n const request: GenerateOptions = {\n provider: config.provider,\n model: config.model,\n messages,\n system: polishSystemPrompt(project),\n // Polish rewrites the whole chapter: generous budget, no reasoning\n // (transform task — the entire budget should go to the body).\n maxTokens: Math.max(config.maxTokens, 20000),\n temperature: 0.5,\n reasoningEffort: ReasoningEffortId('off'),\n }\n yield { frame: 'start' }\n const assembler = new BlockAssembler()\n let streamError: Error | undefined\n for await (const chunk of ctx.llm.stream(request)) {\n assembler.push(chunk)\n if (chunk.type === 'text-delta') yield { frame: 'delta', text: chunk.text }\n }\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n streamError = new Error(`润色失败(${finish.kind}): ${finish.failure.message}`)\n } else if (finish.kind === 'max-tokens') {\n streamError = new Error('润色输出达到 maxTokens 上限')\n }\n const newBody = assembler\n .blocks()\n .filter((block): block is Extract<StreamChunk, { type: 'block-end' }>['block'] & { type: 'text' } => block.type === 'text')\n .map(block => block.text)\n .join('\\n')\n .trim()\n if (streamError !== undefined) throw streamError\n if (newBody.length < 100) throw new Error('润色结果过短,可能失败,请重试')\n\n // Draft mode: keep the original file untouched until the user decides.\n chapter.pendingDraft = newBody\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n yield { frame: 'drafted', chars: newBody.length, draft: newBody }\n}\n\n/** Generate one chapter (streaming). Yields progress frames; persists when done. */\nexport async function* generateChapterStream(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): AsyncGenerator<{ frame: 'start' } | { frame: 'delta'; text: string } | { frame: 'done'; file: string; chars: number }, void, unknown> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) throw new Error(`章节 ${chapterNo} 不在计划中`)\n // Note: the route layer owns the 'generating' status + concurrency guard;\n // this function must not refuse when status is 'generating' (the route sets\n // it before calling us).\n\n // Continuity: previous chapter's ending + its summary (narrative memory).\n let continuity = ''\n const prev = project.chapters.find(c => c.no === chapterNo - 1)\n if (prev?.file !== undefined) {\n const prevPath = join(outputDir, prev.file)\n if (existsSync(prevPath)) {\n const text = readFileSync(prevPath, 'utf8')\n continuity = text.slice(-900)\n }\n }\n const prevSummary = prev?.summary\n // 事实注入:最近 20 条 + 按本章剧情要点低成本检索的「相关旧事实」。\n // 长篇后旧设定可能被挤出最近 20 条,相关检索保证关键状态不写飞。\n const allFacts = project.facts ?? []\n const recentFacts = allFacts.slice(-20).map(f => f.text)\n const beatsText = chapter.beats\n const relatedFacts = allFacts\n .slice(-120)\n .filter(f => {\n const head = f.text.slice(0, 24)\n for (let i = 0; i + 3 <= head.length; i++) {\n const tri = head.slice(i, i + 3)\n if (tri.trim() !== '' && beatsText.includes(tri)) return true\n }\n return false\n })\n .slice(-15)\n .map(f => `[第${f.chapterNo}章] ${f.text}`)\n\n const user = [\n `现在写第 ${chapter.no} 章,标题《${chapter.title}》。`,\n `本章剧情要点:${chapter.beats}`,\n '',\n recentFacts.length > 0\n ? `本书已确立的事实(新写内容不得与之矛盾):\\n${recentFacts.join('\\n')}`\n : '',\n relatedFacts.length > 0\n ? `本章相关的既往事实(同样不得违背):\\n${relatedFacts.join('\\n')}`\n : '',\n prevSummary !== undefined && prevSummary !== ''\n ? `上一章摘要:${prevSummary}`\n : '',\n continuity !== ''\n ? `上一章结尾(用于衔接,不要复述):\\n${continuity}`\n : '这是第一章,注意开篇要有吸引力。',\n '',\n `请写 ${chapter.targetChars} 字左右的正文,只输出正文。`,\n ].filter(line => line !== '').join('\\n')\n\n const messages: Message[] = [createUserMessage({\n content: [{ type: 'text', text: user }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n })]\n const request: GenerateOptions = {\n provider: config.provider,\n model: config.model,\n messages,\n system: writeSystemPrompt(project),\n // Full-chapter output: budget generously (4000 chars ≈ 8-12k tokens,\n // plus the model's reasoning channel).\n maxTokens: Math.max(config.maxTokens, 20000),\n temperature: 0.85,\n }\n\n yield { frame: 'start' }\n\n const assembler = new BlockAssembler()\n let streamError: Error | undefined\n for await (const chunk of ctx.llm.stream(request)) {\n assembler.push(chunk)\n if (chunk.type === 'text-delta') {\n yield { frame: 'delta', text: chunk.text }\n }\n }\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n streamError = new Error(`生成失败(${finish.kind}): ${finish.failure.message}`)\n } else if (finish.kind === 'max-tokens') {\n streamError = new Error('达到 maxTokens 上限,正文可能不完整,请增大 maxTokens 后重试')\n }\n const body = assembler\n .blocks()\n .filter((block): block is Extract<StreamChunk, { type: 'block-end' }>['block'] & { type: 'text' } => block.type === 'text')\n .map(block => block.text)\n .join('\\n')\n .trim()\n if (streamError !== undefined) throw streamError\n if (body.length < 100) throw new Error('生成内容过短,可能失败,请重试')\n\n // Write the chapter file.\n const fileName = chapterFileName(chapter)\n mkdirSync(outputDir, { recursive: true })\n writeFileSync(join(outputDir, fileName), `# 第${chapter.no}章 ${chapter.title}\\n\\n${body}\\n`, 'utf8')\n\n chapter.status = 'written'\n chapter.chars = body.length\n chapter.file = fileName\n chapter.error = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n\n yield { frame: 'done', file: fileName, chars: body.length }\n}\n\n/** Generate a chapter summary (narrative memory). */\nexport async function summarizeChapter(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): Promise<string> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) throw new Error(`章节 ${chapterNo} 不在计划中`)\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) throw new Error(`章节 ${chapterNo} 的正文文件不存在`)\n const system = [\n '你是一位网文编辑。请为下面一章写一段 120-200 字的摘要,供后续章节写作时保持连贯性。',\n '摘要必须包含:本章发生的关键事件、主角状态变化(境界/资源/伤势/心境)、新增的伏笔或线索、角色关系变化。',\n '用客观陈述句,不要评价,不要剧透式感叹。只输出摘要正文。',\n ].join('\\n')\n const user = body.replace(/^#\\s+.*$/m, '').trim()\n const summary = await complete(ctx, config, { system, user, temperature: 0.3, maxTokens: Math.max(config.maxTokens, 4000) })\n chapter.summary = summary.slice(0, 500)\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return chapter.summary\n}\n\n/**\n * 摘要 + 事实抽取合并为一次 LLM 调用(省一次调用与一次正文输入,\n * 批量生成时整体开销约省 25%)。\n * @returns 摘要与新增事实条数(失败返回空,调用方 best-effort)。\n */\nexport async function summarizeAndExtractFacts(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): Promise<{ summary: string; factCount: number }> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) return { summary: '', factCount: 0 }\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) return { summary: '', factCount: 0 }\n const system = [\n '你是一位网文编辑。请为下面一章做两件事,输出合法 JSON 对象:',\n '{\"summary\": \"120-200字摘要,含关键事件/主角状态变化(境界资源伤势心境)/新增伏笔线索/角色关系变化,客观陈述不评价\", \"facts\": [\"已确立事实1\", \"…3-6条\"]}',\n 'facts 指:本章明确写出的、对后续有约束力的事实——人物当前状态、重要关系变化、地点与时间线、已落地或新增的伏笔线索、关键道具去向。',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n const user = body.replace(/^#\\s+.*$/m, '').trim()\n const text = await complete(ctx, config, { system, user, temperature: 0.2, maxTokens: Math.max(config.maxTokens, 5000) })\n const raw = parseJsonObject<{ summary?: unknown; facts?: unknown }>(text)\n const summary = typeof raw.summary === 'string' ? raw.summary.trim().slice(0, 500) : ''\n const factLines = Array.isArray(raw.facts)\n ? raw.facts\n .filter((v): v is string => typeof v === 'string' && v.trim().length > 8)\n .map(v => v.trim().slice(0, 140))\n : []\n if (summary !== '') chapter.summary = summary\n const list = project.facts ?? []\n for (const line of factLines.slice(0, 8)) list.push({ chapterNo, text: line })\n project.facts = list.slice(-300)\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return { summary, factCount: factLines.length }\n}\n\n/**\n * 抽取本章「已确立事实」追加到事实库/时间线(最多 300 条,最新优先)。\n * 事实注入后续章节生成提示词,保证人物状态/境界/资源/关系长期一致。\n * @returns 新增事实条数(失败返回 0,调用方 best-effort)。\n */\nexport async function extractFacts(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): Promise<number> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) return 0\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) return 0\n const system = [\n '你是一位网文编辑。请从本章正文中抽取「已确立事实」,供后续章节保持一致。',\n '事实指:人物当前状态(境界/修为/伤势/资源/心境)、重要关系变化、地点与时间线、已落地或新增的伏笔线索、关键道具去向。',\n '要求:',\n '1. 只抽取本章明确写出的、对后续有约束力的内容;纯心理活动与无关细节不要。',\n '2. 每行一条事实,用客观陈述句,不含主观评价。',\n '3. 输出 3-6 条,每行一条,不要编号、不要前缀、不要解释。',\n ].join('\\n')\n const user = body.replace(/^#\\s+.*$/m, '').trim()\n // v4-flash 推理模型:reasoning channel 也占 maxTokens,预算给足避免截断。\n const text = await complete(ctx, config, { system, user, temperature: 0.2, maxTokens: Math.max(config.maxTokens, 4000) })\n const lines = text.split('\\n')\n .map(line => line.replace(/^[-*\\d.\\s]+/, '').trim())\n .filter(line => line.length > 8)\n .slice(0, 8)\n if (lines.length === 0) return 0\n const facts = project.facts ?? []\n for (const line of lines) facts.push({ chapterNo, text: line.slice(0, 140) })\n project.facts = facts.slice(-300)\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return lines.length\n}\n\n// ------------------------------------------------------------ book audit\n\nconst AUDIT_BATCH_SIZE = 10\n\n/** 单批质检:设定 + 事实库 + 该批章节节选 → 矛盾清单。 */\nasync function auditBatch(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n batch: ChapterPlan[],\n): Promise<AuditIssue[]> {\n const system = [\n '你是一位严谨的网文连续性审校编辑。你会收到一本小说的设定圣经、事实库和一批章节正文节选。',\n '请找出这批章节中的一致性矛盾,例如:',\n '- 人物状态冲突:境界/修为/伤势/资源在同一章内或跨章前后矛盾。',\n '- 设定违背:正文与世界观规则、金手指规则、写作红线冲突。',\n '- 时间线错乱:事件顺序、时间跨度、地点移动不合逻辑。',\n '- 细节穿帮:人名/地名/物品/数字前后不一致。',\n '要求:',\n '1. 只报告有实质证据的矛盾,不要泛泛而谈写作质量问题。',\n '2. 每条必须定位到具体章节号。',\n '3. 输出必须是合法 JSON 数组,格式:[{\"chapterNo\": 章节号, \"severity\": \"high|medium|low\", \"item\": \"矛盾描述\", \"suggestion\": \"修改建议\"}]',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n const factsBlock = (project.facts ?? []).slice(-60).map(f => `[第${f.chapterNo}章] ${f.text}`).join('\\n')\n const chapterBlocks = batch.map(c => {\n const body = readChapterFile(outputDir, c)\n const excerpt = (body ?? '').replace(/^#\\s+.*$/m, '').trim().slice(0, 700)\n return `【第${c.no}章《${c.title}》】\\n${excerpt}`\n }).join('\\n\\n')\n const user = [\n '请对以下小说做一致性质检。',\n project.bible !== undefined\n ? '设定圣经:\\n' + [\n project.bible.worldRules.length > 0 ? `世界规则:\\n${project.bible.worldRules.map(r => `- ${r}`).join('\\n')}` : '',\n project.bible.redLines.length > 0 ? `写作红线:\\n${project.bible.redLines.map(r => `- ${r}`).join('\\n')}` : '',\n project.bible.characters.length > 0 ? `角色:\\n${project.bible.characters.map(ch => `- ${ch.name}(${ch.traits.join('、')})`).join('\\n')}` : '',\n ].filter(s => s !== '').join('\\n')\n : '',\n factsBlock !== '' ? `已确立事实库:\\n${factsBlock}` : '',\n `正文节选(每章前 700 字):\\n${chapterBlocks}`,\n '只输出 JSON 数组。',\n ].filter(s => s !== '').join('\\n\\n')\n const text = await complete(ctx, config, { system, user, temperature: 0.2, maxTokens: Math.max(config.maxTokens, 12000) })\n const parsed = parseJsonArray<Record<string, unknown>>(text)\n const issues: AuditIssue[] = []\n for (const entry of parsed) {\n const item = typeof entry.item === 'string' ? entry.item : ''\n if (item === '') continue\n issues.push({\n chapterNo: Number(entry.chapterNo) || 0,\n severity: ['high', 'medium', 'low'].includes(entry.severity as string)\n ? entry.severity as AuditIssue['severity']\n : 'medium',\n item,\n suggestion: typeof entry.suggestion === 'string' ? entry.suggestion : '',\n })\n }\n return issues\n}\n\n/** 全书一致性质检:LLM 分批扫描已生成章节 + 设定 + 事实库,聚合矛盾清单。 */\nexport async function auditBook(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n): Promise<AuditIssue[]> {\n const written = project.chapters.filter(c => c.status !== 'pending' && c.status !== 'generating')\n if (written.length === 0) return []\n // 分批:每批 AUDIT_BATCH_SIZE 章,避免超长后单次爆上下文。\n const all: AuditIssue[] = []\n for (let i = 0; i < written.length; i += AUDIT_BATCH_SIZE) {\n const batch = written.slice(i, i + AUDIT_BATCH_SIZE)\n try {\n all.push(...await auditBatch(ctx, config, project, outputDir, batch))\n } catch { /* 单批失败不阻断其余批次 */ }\n }\n return all.slice(0, 50)\n}\n\n/** 小说简介:AI 生成或按已写开头补全(面向读者的作品门面)。 */\nexport async function generateBlurb(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n partial = '',\n): Promise<string> {\n const system = [\n '你是一位网文平台编辑,擅长写抓人的作品简介。',\n '要求:',\n '1. 120-250 字,突出核心卖点(金手指/题材/爽点/人设反差),用一两句抛出开局钩子。',\n '2. 不剧透结局与关键反转;语气贴合题材(热血/悬疑/轻松/虐心)。',\n '3. 中文,直接输出简介正文,不要 Markdown、不要引号包裹、不要「简介:」前缀。',\n ].join('\\n')\n const genreBlock = project.bible?.genre !== undefined ? `题材:${project.bible.genre}` : ''\n const volumeBlock = (project.volumes ?? []).slice(0, 3).map(v => v.title).join('、')\n const user = [\n `书名:《${project.bookName}》`,\n genreBlock,\n volumeBlock !== '' ? `卷结构:${volumeBlock}` : '',\n `已写章节数:${project.chapters.filter(c => c.status !== 'pending' && c.status !== 'generating').length}`,\n '大纲节选:\\n' + project.outline.slice(0, 2500),\n partial.trim() !== ''\n ? `已有开头草稿(请保留其内容与语气,续写补全为完整简介):\\n${partial.trim()}`\n : '请全量生成一份完整简介。',\n ].filter(s => s !== '').join('\\n\\n')\n const text = await complete(ctx, config, { system, user, temperature: 0.7, maxTokens: Math.max(config.maxTokens, 4000) })\n const blurb = text.replace(/^[\"'「『]|[\"'」』]$/g, '').replace(/^简介[::]\\s*/, '').trim().slice(0, 600)\n return blurb\n}\n\n// ---------------------------------------------------------------- world\n\n/**\n * 组装全书上下文包(AI 助手 book_overview 工具)。\n * 分片策略:章节要点默认只给最近 30 章(避免超长后爆上下文);\n * scope='full' 全量;scope=数字 只给该卷章节。\n */\nexport function bookOverview(project: ProjectState, scope: 'recent' | 'full' | number = 'recent'): string {\n const s: string[] = []\n s.push(`书名:${project.bookName}`)\n s.push(`【大纲全文】\\n${project.outline}`)\n if (project.bible !== undefined) {\n const bible = project.bible\n s.push('【设定圣经】')\n if (bible.genre !== '') s.push(`题材基调:${bible.genre}`)\n if (bible.worldRules.length > 0) s.push('世界规则:\\n' + bible.worldRules.map(r => `- ${r}`).join('\\n'))\n if (bible.characters.length > 0) {\n s.push('角色卡:')\n for (const card of bible.characters) {\n const roleName = { protagonist: '主角', supporting: '配角', antagonist: '反派', other: '其他' }[card.role]\n s.push(`- ${card.name}(${roleName}):${card.traits.join('、')}${card.goals !== '' ? `;目标:${card.goals}` : ''}${card.relations !== '' ? `;关系:${card.relations}` : ''}`)\n }\n }\n if (bible.redLines.length > 0) s.push('写作红线:\\n' + bible.redLines.map(r => `- ${r}`).join('\\n'))\n if (bible.style.length > 0) s.push('风格要求:\\n' + bible.style.map(r => `- ${r}`).join('\\n'))\n }\n const worldBlock = renderWorld(project.world)\n if (worldBlock !== '') s.push(worldBlock)\n if (project.volumes !== undefined && project.volumes.length > 0) {\n s.push('【卷结构】')\n for (const v of project.volumes) {\n s.push(`第${v.no}卷《${v.title}》:${v.summary}(章节 ${v.chapterStart}-${v.chapterEnd})`)\n }\n }\n if (project.chapters.length > 0) {\n // 分片:默认最近 30 章;full 全量;数字 = 指定卷。\n const maxNo = project.chapters.reduce((m, c) => Math.max(m, c.no), 0)\n const shown = project.chapters.filter(c => {\n if (scope === 'full') return true\n if (typeof scope === 'number') return c.volume === scope\n return c.no > Math.max(0, maxNo - 30)\n })\n const label = scope === 'full' ? '全部章节(标题/状态/剧情要点/摘要)' : typeof scope === 'number' ? `第 ${scope} 卷章节(标题/状态/剧情要点/摘要)` : `最近 ${shown.length} 章(标题/状态/剧情要点/摘要)`\n s.push(`【${label}】`)\n const statusText: Record<string, string> = { pending: '待生成', generating: '生成中', written: '待审稿', reviewing: '审稿中', approved: '已通过', rejected: '待修订', error: '失败' }\n for (const c of shown) {\n s.push(`第${c.no}章《${c.title}》[${statusText[c.status] ?? c.status}]${c.chars !== undefined ? ` ${c.chars}字` : ''}\\n剧情要点:${c.beats}\\n摘要:${c.summary ?? '无'}`)\n }\n if (scope !== 'full' && project.chapters.length > shown.length) {\n s.push(`(还有 ${project.chapters.length - shown.length} 章未列出,可用 scope=volume:N 查看指定卷)`)\n }\n }\n if ((project.facts ?? []).length > 0) {\n s.push('【事实库(最近 40 条;更多用 facts_query 检索)】')\n for (const f of (project.facts ?? []).slice(-40)) {\n s.push(`- [第${f.chapterNo}章] ${f.text}`)\n }\n }\n if (project.foreshadows.length > 0) {\n s.push('【伏笔】')\n for (const f of project.foreshadows) {\n s.push(`- [${f.status}] ${f.description}${f.targetChapter !== undefined ? `(预计 ${f.targetChapter} 章回收)` : ''}`)\n }\n }\n if (project.blurb !== undefined && project.blurb !== '') s.push(`【小说简介】${project.blurb}`)\n return s.join('\\n\\n')\n}\n\n/** 一条影响分析结果(改动波及处)。 */\nexport interface ImpactItem {\n /** 位置:章节号 / 大纲 / 设定圣经 / 大世界 / 事实库 / 简介。 */\n location: string\n /** 原文片段(定位用)。 */\n quote: string\n /** 修改建议。 */\n suggestion: string\n /** must = 必须同步改;optional = 建议改;note = 备注(如保留旧称作古称)。 */\n kind: 'must' | 'optional' | 'note'\n}\n\n/**\n * 影响分析:LLM 扫描全书(大纲/设定/大世界/事实库/已写章节),\n * 定位一次改动波及的所有位置。助手在修改后主动调用,做连锁维护。\n */\nexport async function analyzeImpact(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n change: string,\n): Promise<ImpactItem[]> {\n const system = [\n '你是一位网文一致性审校。作者要做一处修改,请找出这次改动会波及的所有位置(设定、大纲、已写章节正文、事实库、简介中可能因此过时或矛盾的内容)。',\n '输出必须是合法 JSON 数组,格式:[{\"location\": \"位置(第N章/大纲/设定圣经-世界规则/大世界-境界/事实库/简介)\", \"quote\": \"原文片段(20-60字)\", \"suggestion\": \"修改建议\", \"kind\": \"must|optional|note\"}]',\n 'kind 含义:must=必须同步改否则矛盾;optional=建议改(影响观感);note=备注(如旧称保留为古称、或无需改但需知晓)。',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n const written = project.chapters.filter(c => c.status !== 'pending' && c.status !== 'generating')\n // 轻量 base:大纲节选 + 道藏要点 + 编年录最近 40 条(替代全量 bookOverview,\n // 定位主要靠各批章节节选原文)。\n const base = [\n `要做的修改:${change}`,\n '以下为全书设定与规则要点(章节为分批节选):',\n `大纲节选:\\n${project.outline.slice(0, 2000)}`,\n project.bible !== undefined\n ? `道藏:${project.bible.worldRules.length} 条世界规则 / ${project.bible.redLines.length} 条红线 / 人物 ${project.bible.characters.map(c => c.name).join('、')}`\n : '',\n (project.facts ?? []).length > 0\n ? `编年录最近 40 条:\\n${(project.facts ?? []).slice(-40).map(f => `[第${f.chapterNo}章] ${f.text}`).join('\\n')}`\n : '',\n ].filter(s => s !== '').join('\\n\\n')\n const items: ImpactItem[] = []\n // 分批扫描章节正文(每批 8 章),聚合影响清单,避免超长后爆上下文。\n const IMPACT_BATCH_SIZE = 8\n for (let i = 0; i < written.length; i += IMPACT_BATCH_SIZE) {\n const batch = written.slice(i, i + IMPACT_BATCH_SIZE)\n const chapterBlock = batch.map(c => {\n const body = readChapterFile(outputDir, c)\n const excerpt = (body ?? '').replace(/^#\\s+.*$/m, '').trim().slice(0, 500)\n return `【第${c.no}章《${c.title}》】\\n${excerpt}`\n }).join('\\n\\n')\n const user = `${base}\\n\\n本批章节(第 ${batch[0]!.no}-${batch[batch.length - 1]!.no} 章):\\n${chapterBlock}\\n\\n只输出 JSON 数组。`\n try {\n const text = await complete(ctx, config, { system, user, temperature: 0.2, maxTokens: Math.max(config.maxTokens, 12000) })\n for (const entry of parseJsonArray<Record<string, unknown>>(text)) {\n const quote = typeof entry.quote === 'string' ? entry.quote.trim() : ''\n if (quote === '') continue\n items.push({\n location: typeof entry.location === 'string' ? entry.location : '未定位',\n quote: quote.slice(0, 120),\n suggestion: typeof entry.suggestion === 'string' ? entry.suggestion : '',\n kind: entry.kind === 'must' || entry.kind === 'optional' || entry.kind === 'note' ? entry.kind : 'optional',\n })\n }\n } catch { /* 单批失败不阻断其余批次 */ }\n }\n return items.slice(0, 30)\n}\n\n/** 把大世界结构化数据渲染成提示词块(境界体系按顺序强约束)。 */\nexport function renderWorld(world: WorldState | undefined): string {\n if (world === undefined) return ''\n const sections: string[] = ['==================== 大世界(结构化设定,写作时严格遵守) ====================']\n if (world.realms.length > 0) {\n sections.push('境界体系(由低到高,不得随意跳级或自创境界):')\n world.realms.forEach((realm, i) => {\n sections.push(`${i + 1}. ${realm.name}${realm.description !== '' ? ` — ${realm.description}` : ''}`)\n })\n }\n if (world.regions.length > 0) {\n sections.push('地理区域:')\n for (const region of world.regions) {\n sections.push(`- ${region.name}${region.description !== '' ? `:${region.description}` : ''}${region.faction !== undefined && region.faction !== '' ? `(势力:${region.faction})` : ''}`)\n }\n }\n if (world.factions.length > 0) {\n sections.push('势力分布:')\n for (const faction of world.factions) {\n sections.push(`- ${faction.name}(${faction.kind})${faction.description !== '' ? `:${faction.description}` : ''}${faction.region !== undefined && faction.region !== '' ? `(驻地:${faction.region})` : ''}`)\n }\n }\n return sections.join('\\n')\n}\n\n/** AI 提炼大世界:从大纲 + 设定圣经生成结构化境界体系/区域/势力。 */\nexport async function extractWorld(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n): Promise<WorldState> {\n const system = [\n '你是一位网文世界观架构师。请根据小说大纲与设定圣经,提炼结构化「大世界」数据。',\n '输出必须是合法 JSON 对象:',\n '{\"realms\": [{\"name\": \"境界名\", \"description\": \"突破条件/寿命/标志等\"}], \"regions\": [{\"name\": \"区域名\", \"description\": \"描述\", \"faction\": \"关联势力名或空\"}], \"factions\": [{\"name\": \"势力名\", \"kind\": \"宗门/家族/王朝/组织等\", \"description\": \"描述\", \"region\": \"驻地区域或空\"}]}',\n '要求:',\n '1. realms 按由低到高顺序排列(修仙题材必须含完整境界链;无境界设定的题材可输出空数组)。',\n '2. 数量贴合大纲:realms 3-12 个,regions 2-10 个,factions 2-10 个。',\n '3. 内容严格来自大纲与设定圣经,不要凭空发明与大纲冲突的设定。',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n const bibleBlock = project.bible !== undefined\n ? [\n project.bible.genre !== '' ? `题材:${project.bible.genre}` : '',\n project.bible.worldRules.length > 0 ? `世界规则:\\n${project.bible.worldRules.map(r => `- ${r}`).join('\\n')}` : '',\n ].filter(s => s !== '').join('\\n')\n : ''\n const user = [\n '请为这部小说提炼大世界数据。',\n `书名:《${project.bookName}》`,\n bibleBlock !== '' ? bibleBlock : '',\n '大纲:\\n' + project.outline.slice(0, 5000),\n '只输出 JSON 对象。',\n ].filter(s => s !== '').join('\\n\\n')\n const text = await complete(ctx, config, { system, user, temperature: 0.3, maxTokens: Math.max(config.maxTokens, 12000) })\n const raw = parseJsonObject<{ realms?: unknown; regions?: unknown; factions?: unknown }>(text)\n const str = (value: unknown): string => typeof value === 'string' ? value.trim() : ''\n const objArray = (value: unknown): Record<string, unknown>[] =>\n Array.isArray(value) ? value.filter((v): v is Record<string, unknown> => typeof v === 'object' && v !== null) : []\n const world: WorldState = {\n realms: objArray(raw.realms).map(entry => ({\n name: str(entry.name).slice(0, 20) || '未命名境界',\n description: str(entry.description).slice(0, 200),\n })).filter(r => r.name !== '未命名境界' || r.description !== ''),\n regions: objArray(raw.regions).map(entry => ({\n name: str(entry.name).slice(0, 30) || '未命名区域',\n description: str(entry.description).slice(0, 200),\n faction: str(entry.faction).slice(0, 30),\n })).filter(r => r.name !== '未命名区域' || r.description !== ''),\n factions: objArray(raw.factions).map(entry => ({\n name: str(entry.name).slice(0, 30) || '未命名势力',\n kind: str(entry.kind).slice(0, 20) || '组织',\n description: str(entry.description).slice(0, 200),\n region: str(entry.region).slice(0, 30),\n })).filter(f => f.name !== '未命名势力' || f.description !== ''),\n }\n return world\n}\n\n/**\n * 事实库回填:对历史已生成章节批量抽取事实(无事实记录的旧章节)。\n * @returns 回填的章节数。\n */\nexport async function backfillFacts(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n): Promise<number> {\n const have = new Set((project.facts ?? []).map(f => f.chapterNo))\n let filled = 0\n for (const chapter of project.chapters) {\n if (chapter.status === 'pending' || chapter.status === 'generating') continue\n if (chapter.file === undefined || have.has(chapter.no)) continue\n try {\n const n = await extractFacts(ctx, config, project, outputDir, chapter.no)\n if (n > 0) filled++\n } catch { /* best-effort per chapter */ }\n have.add(chapter.no)\n }\n return filled\n}\n\n/**\n * 角色卡刷新:出场统计由服务端从正文精确计算(角色名出现过的章节数、\n * 最近出现章节),LLM 只负责聚合「当前状态」一句话。\n */\nexport async function refreshCharacters(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n): Promise<RoleStatusCard[]> {\n const roster = project.bible?.characters ?? []\n const facts = project.facts ?? []\n if (roster.length === 0 && facts.length === 0) return []\n\n // 服务端精确出场统计:遍历已写章节正文,统计每个角色名出现过的章节。\n const stat = new Map<string, { chapters: Set<number>; last: number }>()\n const known = roster.map(card => card.name)\n for (const chapter of project.chapters) {\n if (chapter.status === 'pending' || chapter.status === 'generating') continue\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) continue\n for (const name of known) {\n if (body.includes(name)) {\n const entry = stat.get(name) ?? { chapters: new Set<number>(), last: 0 }\n entry.chapters.add(chapter.no)\n if (chapter.no > entry.last) entry.last = chapter.no\n stat.set(name, entry)\n }\n }\n }\n\n // LLM 只聚合状态:名单(含 traits)+ 事实库 → [{name, status}]\n let statuses = new Map<string, string>()\n if (facts.length > 0) {\n const system = [\n '你是一位网文角色档案管理员。请根据「角色名单」与「已确立事实库」,为每个角色输出「当前状态」一句话(境界/修为/伤势/资源/心境)。',\n '输出必须是合法 JSON 数组,格式:[{\"name\": \"角色名\", \"status\": \"当前状态一句话\"}]',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n const rosterBlock = roster.map(ch => `- ${ch.name}(${ch.traits.join('、')})`).join('\\n')\n const factsBlock = facts.map(f => `[第${f.chapterNo}章] ${f.text}`).join('\\n')\n const user = [\n `角色名单:\\n${rosterBlock}`,\n `已确立事实库(${facts.length} 条):\\n${factsBlock.slice(-6000)}`,\n '只输出 JSON 数组。',\n ].join('\\n\\n')\n try {\n const text = await complete(ctx, config, { system, user, temperature: 0.2, maxTokens: Math.max(config.maxTokens, 8000) })\n for (const entry of parseJsonArray<Record<string, unknown>>(text)) {\n const name = typeof entry.name === 'string' ? entry.name : ''\n if (name !== '' && typeof entry.status === 'string') statuses.set(name, entry.status)\n }\n } catch { /* status 聚合失败则只给出场统计 */ }\n }\n\n // 合并:出场统计(精确)+ 状态(LLM)+ 名单角色补全。\n const cards: RoleStatusCard[] = []\n const roleOf = (name: string): string => roster.find(c => c.name === name)?.role ?? 'other'\n for (const card of roster) {\n const entry = stat.get(card.name)\n cards.push({\n name: card.name,\n role: card.role,\n status: statuses.get(card.name) ?? '',\n lastChapter: entry?.last ?? 0,\n appearances: entry?.chapters.size ?? 0,\n })\n }\n // 名单外的角色(从事实库中识别到但不在设定圣经名单)仅当有出场统计时补充。\n for (const [name, entry] of stat) {\n if (!cards.some(c => c.name === name)) {\n cards.push({\n name,\n role: roleOf(name),\n status: statuses.get(name) ?? '',\n lastChapter: entry.last,\n appearances: entry.chapters.size,\n })\n }\n }\n return cards\n}\n\n// ------------------------------------------------------------- foreshadows\n\n/** System prompt for foreshadow suggestions. */\nfunction foreshadowSystemPrompt(): string {\n return [\n '你是一位网文伏笔设计师。你会收到大纲和已写的章节信息,请为小说建议 3-8 条值得埋设的伏笔。',\n '要求:',\n '1. 伏笔必须有明确的回收价值(推动主线、人物弧光、世界观揭秘)。',\n '2. 描述要具体,指出埋设章节与预计回收章节(可空缺)。',\n '3. 优先从大纲的暗线(如记忆代价、残片收集、身世谜团)中提炼。',\n '输出必须是合法 JSON 数组:',\n '[{\"description\": \"伏笔描述\", \"plantedChapter\": 章节号或null, \"targetChapter\": 章节号或null}]',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n}\n\n/** Suggest foreshadows from the outline + plan. */\nexport async function suggestForeshadows(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n): Promise<Foreshadow[]> {\n const user = [\n '请为下面这部小说设计伏笔。',\n `大纲:\\n${project.outline}`,\n `已规划章节数:${project.chapters.length}`,\n ].join('\\n')\n const text = await complete(ctx, config, { system: foreshadowSystemPrompt(), user, temperature: 0.5, maxTokens: Math.max(config.maxTokens, 12000) })\n const parsed = parseJsonArray<Record<string, unknown>>(text)\n const existing = new Set(project.foreshadows.map(f => f.description))\n const created: Foreshadow[] = []\n for (const entry of parsed) {\n if (typeof entry !== 'object' || entry === null) continue\n const description = typeof entry.description === 'string' ? entry.description.trim() : ''\n if (description === '' || existing.has(description)) continue\n existing.add(description)\n created.push({\n id: `fs-${Date.now().toString(36)}-${created.length}`,\n description: description.slice(0, 200),\n plantedChapter: typeof entry.plantedChapter === 'number' ? entry.plantedChapter : undefined,\n targetChapter: typeof entry.targetChapter === 'number' ? entry.targetChapter : undefined,\n status: 'planned',\n })\n }\n project.foreshadows.push(...created)\n project.updatedAt = new Date().toISOString()\n return created\n}\n\n// -------------------------------------------------------------- style asset\n\n/**\n * 写法引擎:从样本文本提取一份写法资产(叙事风格规则)。\n * @returns 提取出的风格规则(未持久化,由调用方存入 project.assets)。\n */\nexport async function extractStyleAsset(\n ctx: Context,\n config: NovelConfig,\n sampleText: string,\n): Promise<{ proseRules: string[]; dialogueRules: string[]; descriptionRules: string[]; boundaries: string[] }> {\n const user = `请分析下面这段样本文本,提炼其叙事风格规则:\\n\\n${sampleText}`\n const text = await complete(ctx, config, { system: styleEngineSystemPrompt(), user, temperature: 0.3, maxTokens: Math.max(config.maxTokens, 12000) })\n const raw = parseJsonObject<{ proseRules?: unknown; dialogueRules?: unknown; descriptionRules?: unknown; boundaries?: unknown }>(text)\n const strArray = (value: unknown): string[] =>\n Array.isArray(value) ? value.filter((v): v is string => typeof v === 'string' && v.trim() !== '') : []\n const result = {\n proseRules: strArray(raw.proseRules),\n dialogueRules: strArray(raw.dialogueRules),\n descriptionRules: strArray(raw.descriptionRules),\n boundaries: strArray(raw.boundaries),\n }\n if (result.proseRules.length + result.dialogueRules.length + result.descriptionRules.length + result.boundaries.length === 0) {\n throw new Error('写法提取失败:模型没有返回有效规则')\n }\n return result\n}\n\n// ------------------------------------------------------------------ export\n\n/** Export the whole book as one txt/md file. */\nexport function exportBook(outputDir: string, project: ProjectState, format: 'txt' | 'md'): { file: string; chars: number; chapters: number } {\n const parts: string[] = []\n if (format === 'md') {\n parts.push(`# ${project.bookName}\\n`)\n } else {\n parts.push(project.bookName, '')\n }\n const done = project.chapters.filter(c => c.file !== undefined)\n for (const chapter of done) {\n const body = readChapterFile(outputDir, chapter) ?? ''\n if (format === 'md') {\n parts.push(`\\n## 第${chapter.no}章 ${chapter.title}\\n`, body.trim(), '')\n } else {\n parts.push('', `第${chapter.no}章 ${chapter.title}`, '', body.trim(), '')\n }\n }\n const content = parts.join('\\n')\n const ext = format === 'md' ? 'md' : 'txt'\n const file = `《${safeFileName(project.bookName)}》全本.${ext}`\n writeFileSync(join(outputDir, file), content, 'utf8')\n return { file, chars: content.length, chapters: done.length }\n}\n","/**\n * AI assistant engine — a conversational editor over the novel project.\n *\n * The user talks to the assistant about plot, characters, settings; the\n * assistant can reply in prose AND emit action directives that the host\n * executes (rewrite a paragraph, edit the bible, regenerate a chapter,\n * export the book, ...). Conversation history persists next to the project\n * as NDJSON, so a reload keeps the thread.\n *\n * Action protocol: the model emits a line of the form\n * <dsh-action name=\"toolName\">{jsonArgs}</dsh-action>\n * anywhere in its reply. The host strips it, executes the tool, appends the\n * result as a tool-role message, and continues the loop (bounded rounds).\n */\n\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, appendFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createUserMessage, createAssistantMessage, BlockAssembler, type GenerateOptions, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm'\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { AssistantMessage, NovelConfig, ProjectState } from './protocol.ts'\nimport { emptyProjectAssets } from './assets.ts'\nimport {\n analyzeImpact,\n bookOverview,\n chapterFileName,\n exportBook,\n generateChapterStream,\n readChapterFile,\n reviewChapter,\n saveProject,\n summarizeAndExtractFacts,\n} from './engine.ts'\nimport { rewriteChapterStream } from './engine.ts'\n\n/** History file name inside the output dir. */\nexport const ASSISTANT_HISTORY_FILE = 'novel-assistant.jsonl'\n\n/** Max tool-call rounds per user turn (safety bound). */\nconst MAX_TOOL_ROUNDS = 6\n\n/** Max history messages kept in context (older ones summarized away). */\nconst MAX_HISTORY_MESSAGES = 24\n\n// ------------------------------------------------------------------ history\n\n/** Load the persisted conversation (empty when none). */\nexport function loadAssistantHistory(outputDir: string): AssistantMessage[] {\n const file = join(outputDir, ASSISTANT_HISTORY_FILE)\n if (!existsSync(file)) return []\n const messages: AssistantMessage[] = []\n try {\n for (const line of readFileSync(file, 'utf8').split('\\n')) {\n if (line.trim() === '') continue\n try {\n const parsed = JSON.parse(line) as AssistantMessage\n if (typeof parsed.role === 'string' && typeof parsed.content === 'string') messages.push(parsed)\n } catch { /* skip malformed line */ }\n }\n } catch { /* unreadable history -> start fresh */ }\n return messages\n}\n\n/** Append one message to the persisted history. Tool payloads (e.g. full\n * outline / chapter text) are capped so the jsonl and later LLM context\n * don't grow unboundedly. */\nfunction appendHistory(outputDir: string, message: AssistantMessage): void {\n mkdirSync(outputDir, { recursive: true })\n const entry: AssistantMessage = message.role === 'tool' && message.content.length > 4000\n ? { ...message, content: message.content.slice(0, 4000) + '\\n…(已截断,如需完整内容请重新调用工具)' }\n : message\n appendFileSync(join(outputDir, ASSISTANT_HISTORY_FILE), JSON.stringify(entry) + '\\n', 'utf8')\n}\n\n/** 清空助手对话记录(删除历史文件)。 */\nexport function clearAssistantHistory(outputDir: string): void {\n const file = join(outputDir, ASSISTANT_HISTORY_FILE)\n if (existsSync(file)) rmSync(file, { force: true })\n}\n\n// ----------------------------------------------------------------- context\n\n/** Render the project snapshot the assistant sees. */\nfunction renderProjectSnapshot(project: ProjectState): string {\n const sections: string[] = []\n sections.push(`书名:${project.bookName}`)\n sections.push(`大纲节选(如需全文用 outline_text 工具):\\n${project.outline.slice(0, 2500)}`)\n // 写作资产名称化(占位小,需要详情用 assets_status)。\n const assetNames: string[] = []\n if (project.assets?.genre !== undefined) assetNames.push(`题材:${project.assets.genre.name}`)\n if (project.assets?.primaryProgression !== undefined) assetNames.push(`主推进:${project.assets.primaryProgression.name}`)\n if ((project.assets?.styleAssets?.length ?? 0) > 0) assetNames.push(`写法:${project.assets!.styleAssets!.map(s => s.name).join('、')}`)\n if ((project.assets?.antiAiRules?.length ?? 0) > 0) assetNames.push(`文戒自定义:${project.assets!.antiAiRules!.map(r => r.name).join('、')}`)\n if (assetNames.length > 0) sections.push(`【写作资产】${assetNames.join(' · ')}`)\n if (project.bible !== undefined) {\n const bible = project.bible\n sections.push('【设定圣经】')\n if (bible.genre !== '') sections.push(`题材基调:${bible.genre}`)\n if (bible.worldRules.length > 0) sections.push('世界规则:\\n' + bible.worldRules.map(r => `- ${r}`).join('\\n'))\n if (bible.characters.length > 0) {\n sections.push('角色卡:')\n for (const card of bible.characters) {\n const roleName = { protagonist: '主角', supporting: '配角', antagonist: '反派', other: '其他' }[card.role]\n sections.push(`- ${card.name}(${roleName}):${card.traits.join('、')}${card.goals !== '' ? `;目标:${card.goals}` : ''}`)\n }\n }\n if (bible.redLines.length > 0) sections.push('写作红线:\\n' + bible.redLines.map(r => `- ${r}`).join('\\n'))\n }\n if (project.world !== undefined) {\n const world = project.world\n sections.push('【大世界】')\n if (world.realms.length > 0) sections.push('境界体系:' + world.realms.map((r, i) => `${i + 1}.${r.name}(${r.description.slice(0, 40)})`).join(' → '))\n if (world.regions.length > 0) sections.push('区域:' + world.regions.map(r => r.name).join('、'))\n if (world.factions.length > 0) sections.push('势力:' + world.factions.map(f => `${f.name}(${f.kind})`).join('、'))\n }\n if (project.volumes !== undefined && project.volumes.length > 0) {\n sections.push('【卷结构】')\n for (const v of project.volumes) {\n sections.push(`第${v.no}卷《${v.title}》:${v.summary}(章节 ${v.chapterStart}-${v.chapterEnd})`)\n }\n }\n if (project.chapters.length > 0) {\n // 快照只列最近 30 章(长书时防止上下文膨胀;更早章节可用\n // book_overview scope=volume:N 或 chapter_text 按需查看)。\n const shown = project.chapters.slice(-30)\n sections.push(`【章节计划与进度(最近 ${shown.length} 章)】`)\n for (const c of shown) {\n const statusText = { pending: '待生成', generating: '生成中', written: '待审稿', reviewing: '审稿中', approved: '已通过', rejected: '待修订', error: '失败' }[c.status]\n sections.push(`第${c.no}章《${c.title}》[${statusText}]${c.chars !== undefined ? ` ${c.chars}字` : ''}${c.summary !== undefined && c.summary !== '' ? ` 摘要:${c.summary}` : ''}`)\n }\n if (project.chapters.length > shown.length) {\n sections.push(`(还有 ${project.chapters.length - shown.length} 章未列出,可用 book_overview scope=volume:N 查看)`)\n }\n // 最近两章正文节选(讨论细节时参考)\n const written = project.chapters.filter(c => c.status === 'approved' || c.status === 'written')\n const recent = written.slice(-2)\n if (recent.length > 0) {\n sections.push('【最近章节正文节选】')\n for (const c of recent) {\n sections.push(`第${c.no}章《${c.title}》(节选,如需全文用 chapter_text):${c.beats.slice(0, 300)}`)\n }\n }\n }\n if (project.foreshadows.length > 0) {\n sections.push('【伏笔】')\n for (const f of project.foreshadows) {\n sections.push(`- [${f.status}] ${f.description}${f.targetChapter !== undefined ? `(预计 ${f.targetChapter} 章回收)` : ''}`)\n }\n }\n if ((project.facts ?? []).length > 0) {\n sections.push('【已确立事实库(最近 40 条,回答设定问题必须遵守)】')\n for (const f of (project.facts ?? []).slice(-40)) {\n sections.push(`- [第${f.chapterNo}章] ${f.text}`)\n }\n }\n if (project.blurb !== undefined && project.blurb !== '') {\n sections.push(`【小说简介】${project.blurb}`)\n }\n return sections.join('\\n')\n}\n\n/** The assistant system prompt. */\nfunction assistantSystemPrompt(project: ProjectState): string {\n return [\n '你是「编辑老师」——服务这本书作者的资深中文网文编辑。',\n '人设:二十年网文老编辑,懂套路、懂市场、懂节奏,说话直接但句句有用。',\n '座右铭:「书是你的,但坑我替你盯着。」',\n '职责边界:陪作者讨论剧情/人设/世界观/爽点节奏并落地修改、维护全书一致性;不闲聊、不彩虹屁、不无意义长篇大论。',\n '==================== 当前项目快照 ====================',\n renderProjectSnapshot(project),\n '==================== 快照结束 ====================',\n '',\n '工作规则(严格遵守):',\n '1. 全量知情:回答和修改必须基于项目真实数据,禁止编造书中不存在的设定。需要完整信息时,先调用 book_overview 获取全书上下文(大纲全文/设定圣经/大世界/事实库/全部章节要点/伏笔/简介);需要某章正文用 chapter_text。',\n '2. 修改流程:改前用一句话说明意图 → 执行工具 → 改后简要汇报。',\n '3. 连锁维护:改动可能波及其它位置(其它章节、设定、事实库、简介)时,执行后主动调用 impact_analysis 分析影响面,并把「必须同步」的项一并处理或明确提示作者逐项确认。',\n '4. 删除红线:删除章节、清空设定等破坏性操作必须等作者明确同意。',\n '5. 品质门槛:建议必须具体——指出问题在哪一章、哪一段、哪一句,并给出可落地的改法;禁止\"建议增强冲突\"这类空话。',\n '6. 设定忠诚:忠于大纲、设定圣经、大世界、事实库;发现书中已有内容与设定冲突时,主动指出并给修正方案。',\n '7. 中文回复,简洁有干货。',\n '',\n '可用工具:',\n '- book_overview:{\"scope\": \"recent|full|volume:2\"(可选,默认 recent)}。返回全书上下文包(大纲/道藏/大世界/章节要点/事实库/伏笔/简介)。recent=最近30章;full=全部章节(书很长时慎用);volume:N=只看第N卷。',\n '- facts_query:{\"keyword\": \"关键词\"}。从编年录(事实库)按关键词检索相关事实(如灵石、境界名、人物名)。',\n '- impact_analysis:{\"change\": \"要做的修改描述\"}。分析这次改动会波及哪些位置,返回影响清单(定位到章节/设定/事实库)。',\n '- outline_text:无参数。返回当前大纲全文。',\n '- outline_replace:{\"old\": \"要替换的原文片段\", \"new\": \"新文本\"}。在大纲中替换一段文字(old 必须能在大纲中找到)。',\n '- bible_set_rule:{\"index\": 序号(0起), \"text\": \"新规则文本\"} 或 {\"append\": \"追加的规则\"}。修改设定圣经的世界规则。',\n '- bible_set_redline:同上,修改写作红线。',\n '- chapter_text:{\"no\": 章节号}。返回该章正文。',\n '- chapter_rewrite:{\"no\": 章节号, \"instructions\": \"修改要求\", \"target\": \"原文片段(可选,留空整章)\"}。按讨论结果修订章节;给了 target 只改该自然段。',\n '- chapter_generate:{\"no\": 章节号}。重新生成该章。',\n '- chapter_review:{\"no\": 章节号}。对该章执行 AI 审稿。',\n '- foreshadow_add:{\"description\": \"伏笔描述\", \"targetChapter\": 预计回收章(可选)}。新增伏笔。',\n '- foreshadow_update:{\"id\": \"伏笔id\", \"status\": \"planned|planted|progressing|resolved|abandoned\"}。更新伏笔状态。',\n '- export_txt:无参数。导出全本 TXT。',\n '- assets_status:无参数。查看本书当前写作资产(题材/推进模式/反AI规则/写法)。',\n '- assets_set_genre:{\"name\": \"题材名\", \"description\": \"题材说明(可选)\"}。设置本书题材基底。',\n '- assets_set_progression:{\"name\": \"模式名\", \"driver\": \"驱动力\", \"primary\": true/false}。设置主/辅助推进模式。',\n '- assets_add_rule:{\"name\": \"规则名(可选)\", \"avoid\": \"要避免的表达问题\", \"fix\": \"修正方向(可选)}。新增反 AI 规则。',\n '',\n '回答质量要求(非常重要):',\n '- 具体:回答必须引用项目里的真实内容(人名、境界、章节、伏笔、设定),禁止空泛套话。快照里没有的信息,先调用工具获取(chapter_text / outline_text)再回答。',\n '- 专业:给建议时说明理由,指出问题所在章节/段落,给出可直接落地的修改方案(改什么、怎么改)。',\n '- 主动:作者说\"改一下\",主动调用对应工具执行,不要只给建议不动手;执行前用一句话说明意图,执行后简短汇报结果。',\n '- 忠于设定:以大纲、设定圣经、事实库为准,不得自相矛盾;发现问题(如剧情与设定冲突)主动指出。',\n '- 中文回复;文字量适中,别啰嗦。',\n '',\n '使用规则(非常重要):',\n '- 当你想执行任何工具时,你的【整个回复】必须只包含动作指令标签,格式如下(不要有任何解释文字、不要用自然语言说\"我要去改\",直接输出标签):',\n ' 正确示例:<dsh-action name=\"outline_replace\">{\"old\":\"要替换的原文\",\"new\":\"新文本\"}</dsh-action>',\n ' 正确示例:<dsh-action name=\"chapter_text\">{\"no\":1}</dsh-action>',\n ' 错误示例(绝对不要这样回复):\"好的,我先看一下大纲,马上改。\" ← 这只是文字,不会执行任何操作',\n '- 工具调用是自动的:你输出标签后,宿主会执行并把结果反馈给你,你再基于结果继续。',\n '- 每次回复最多调用 1 个动作;执行结果会反馈给你,你可以继续讨论或再调用。',\n '- 需要先看大纲/章节再决定怎么改?那就先输出一个 outline_text / chapter_text 的标签,等结果回来。',\n '- chapter_rewrite 的 target 参数:从章节正文中复制一小段(一句话或几句话即可),不要带换行、不要带引号,取连续文本片段。',\n '- 如果工具执行失败(例如片段未找到),根据错误信息修正参数后自动重试一次,不要直接放弃或让作者手动操作。',\n '- 修改前先向作者说明你要改什么、为什么;动作执行后简要汇报结果。',\n '- 涉及删除类操作(删除章节、清空设定)必须等作者明确同意。',\n '- 严格忠于设定圣经与大纲;不得自行发明与既有设定冲突的内容。',\n '- 用中文回复。',\n ].join('\\n')\n}\n\n// ------------------------------------------------------------- action exec\n\n/** Execute one action directive. Returns a text result (or throws). */\n/**\n * Execute one action directive as an async generator: yields live progress\n * text (chapter text being generated/rewritten), then yields the final result\n * string. Throws on failure.\n */\nexport async function* executeAction(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n name: string,\n args: Record<string, unknown>,\n): AsyncGenerator<string, string, unknown> {\n const str = (value: unknown): string => typeof value === 'string' ? value : ''\n const num = (value: unknown): number | undefined => typeof value === 'number' ? value : undefined\n\n /** Forward live text deltas from a streaming chapter job (text only). */\n const forward = async function* (stream: AsyncGenerator<{ frame: 'start' } | { frame: 'delta'; text: string } | { frame: 'done'; file: string; chars: number } | { frame: 'drafted'; chars: number; draft: string }, void, unknown>): AsyncGenerator<string, void, unknown> {\n for await (const step of stream) {\n if (step.frame === 'delta') yield step.text\n }\n }\n\n switch (name) {\n case 'book_overview': {\n // 全书上下文包(分片:默认最近 30 章;scope=full 全量;scope=volume:N 指定卷)。\n const scopeArg = str(args.scope)\n const scope = scopeArg === 'full'\n ? 'full' as const\n : /^volume:(\\d+)$/.test(scopeArg)\n ? Number(scopeArg.slice(7))\n : 'recent' as const\n return bookOverview(project, scope)\n }\n case 'facts_query': {\n // 从编年录(事实库)按关键词检索相关事实。\n const keyword = str(args.keyword).trim()\n if (keyword === '') throw new Error('facts_query 需要 keyword')\n const hits = (project.facts ?? []).filter(f => f.text.includes(keyword)).slice(-30)\n if (hits.length === 0) return `编年录中未找到与「${keyword}」相关的事实记录。`\n return `编年录中与「${keyword}」相关的事实(${hits.length} 条):\\n` + hits.map(f => `- [第${f.chapterNo}章] ${f.text}`).join('\\n')\n }\n case 'impact_analysis': {\n const change = str(args.change)\n if (change === '') throw new Error('impact_analysis 需要 change(要做的修改描述)')\n const items = await analyzeImpact(ctx, config, project, outputDir, change)\n if (items.length === 0) return '影响分析:未发现需要同步修改的位置。'\n const lines = items.map((it, i) => `${i + 1}. [${it.location}]「${it.quote}」${it.suggestion !== '' ? ` → ${it.suggestion}` : ''}(${it.kind === 'must' ? '必须同步' : it.kind === 'optional' ? '建议' : '备注'})`)\n return `影响分析:这次改动波及 ${items.length} 处——\\n${lines.join('\\n')}\\n请据此提示作者逐项处理;章节内的修改可引导作者在工作区查看。`\n }\n case 'outline_text': {\n return project.outline\n }\n case 'outline_replace': {\n const old = str(args.old)\n const next = str(args.new)\n if (old === '' || !project.outline.includes(old)) {\n throw new Error(`大纲中未找到片段「${old.slice(0, 40)}…」`)\n }\n project.outline = project.outline.replace(old, next)\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `大纲已修改:替换了 ${old.length} 字符的片段。`\n }\n case 'bible_set_rule': {\n if (project.bible === undefined) throw new Error('尚无道藏,请先提炼')\n const index = num(args.index)\n if (index !== undefined) {\n project.bible.worldRules[index] = str(args.text)\n } else if (str(args.append) !== '') {\n project.bible.worldRules.push(str(args.append))\n } else {\n throw new Error('bible_set_rule 需要 index+text 或 append')\n }\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `世界规则已更新(当前 ${project.bible.worldRules.length} 条)。`\n }\n case 'bible_set_redline': {\n if (project.bible === undefined) throw new Error('尚无道藏,请先提炼')\n const index = num(args.index)\n if (index !== undefined) {\n project.bible.redLines[index] = str(args.text)\n } else if (str(args.append) !== '') {\n project.bible.redLines.push(str(args.append))\n } else {\n throw new Error('bible_set_redline 需要 index+text 或 append')\n }\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `写作红线已更新(当前 ${project.bible.redLines.length} 条)。`\n }\n case 'chapter_text': {\n const no = num(args.no)\n if (no === undefined) throw new Error('chapter_text 需要 no')\n const chapter = project.chapters.find(c => c.no === no)\n if (chapter === undefined) throw new Error(`章节 ${no} 不存在`)\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) throw new Error(`章节 ${no} 尚未生成`)\n return body\n }\n case 'chapter_rewrite': {\n const no = num(args.no)\n if (no === undefined) throw new Error('chapter_rewrite 需要 no')\n const instructions = str(args.instructions)\n const target = str(args.target)\n for await (const chunk of forward(rewriteChapterStream(ctx, config, project, outputDir, no, instructions, target === '' ? undefined : target))) {\n yield chunk\n }\n // Draft mode: the assistant acts on the user's explicit instruction, so\n // apply the draft immediately — summarize/review must see the new body.\n const chapter = project.chapters.find(c => c.no === no)\n const draft = chapter?.pendingDraft\n if (chapter === undefined || draft === undefined || draft === '') {\n throw new Error(`章节 ${no} 修订后没有产出草稿`)\n }\n const fileName = chapterFileName(chapter)\n mkdirSync(outputDir, { recursive: true })\n writeFileSync(join(outputDir, fileName), `# 第${chapter.no}章 ${chapter.title}\\n\\n${draft}\\n`, 'utf8')\n chapter.pendingDraft = undefined\n chapter.status = 'written'\n chapter.chars = draft.length\n chapter.file = fileName\n chapter.review = undefined\n chapter.error = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n yield '(已采纳修订稿,正在生成章节摘要与编年录…)'\n try {\n await summarizeAndExtractFacts(ctx, config, project, outputDir, no)\n } catch { /* best-effort */ }\n yield '(正在 AI 审稿…)'\n const report = await reviewChapter(ctx, config, project, outputDir, no)\n return `章节 ${no} 已${target === '' ? '整章' : '局部'}修订完成(${project.chapters.find(c => c.no === no)?.chars ?? '?'} 字)。重新审稿:${report.score} 分 — ${report.verdict}`\n }\n case 'chapter_generate': {\n const no = num(args.no)\n if (no === undefined) throw new Error('chapter_generate 需要 no')\n for await (const chunk of forward(generateChapterStream(ctx, config, project, outputDir, no))) {\n yield chunk\n }\n yield '(正在生成章节摘要与编年录…)'\n try {\n await summarizeAndExtractFacts(ctx, config, project, outputDir, no)\n } catch { /* best-effort */ }\n yield '(正在 AI 审稿…)'\n const report = await reviewChapter(ctx, config, project, outputDir, no)\n return `章节 ${no} 已生成(${project.chapters.find(c => c.no === no)?.chars ?? '?'} 字)。审稿:${report.score} 分 — ${report.verdict}`\n }\n case 'chapter_review': {\n const no = num(args.no)\n if (no === undefined) throw new Error('chapter_review 需要 no')\n const report = await reviewChapter(ctx, config, project, outputDir, no)\n const issues = report.issues.map(i => `[${i.severity}] ${i.item} → ${i.suggestion}`).join('\\n')\n return `章节 ${no} 审稿:${report.score} 分 — ${report.verdict}\\n${issues}`\n }\n case 'foreshadow_add': {\n const description = str(args.description)\n if (description === '') throw new Error('foreshadow_add 需要 description')\n const targetChapter = num(args.targetChapter)\n project.foreshadows.push({\n id: `fs-${Date.now().toString(36)}`,\n description,\n targetChapter,\n status: 'planned',\n })\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `已新增伏笔:「${description.slice(0, 50)}」`\n }\n case 'foreshadow_update': {\n const id = str(args.id)\n const status = str(args.status) as 'planned' | 'planted' | 'progressing' | 'resolved' | 'abandoned'\n const target = project.foreshadows.find(f => f.id === id)\n if (target === undefined) throw new Error(`伏笔 ${id} 不存在`)\n if (!['planned', 'planted', 'progressing', 'resolved', 'abandoned'].includes(status)) {\n throw new Error(`非法状态 ${status}`)\n }\n target.status = status\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `伏笔已更新为 ${status}:「${target.description.slice(0, 50)}」`\n }\n case 'export_txt': {\n const result = exportBook(outputDir, project, 'txt')\n return `已导出 TXT:${result.file}(${result.chars} 字,${result.chapters} 章)`\n }\n case 'assets_status': {\n const assets = project.assets\n if (assets === undefined) return '本书尚未配置写作资产。'\n const parts: string[] = []\n if (assets.genre !== undefined) parts.push(`题材:${assets.genre.name}`)\n if (assets.primaryProgression !== undefined) parts.push(`主推进:${assets.primaryProgression.name}`)\n if (assets.auxiliaryProgressions.length > 0) parts.push(`辅助推进:${assets.auxiliaryProgressions.map(m => m.name).join('、')}`)\n if (assets.antiAiRules.length > 0) parts.push(`自定义反AI规则:${assets.antiAiRules.map(r => r.name).join('、')}`)\n if (assets.styleAssets.length > 0) parts.push(`写法资产:${assets.styleAssets.map(s => s.name).join('、')}`)\n return parts.length > 0 ? parts.join('\\n') : '本书尚未配置写作资产。'\n }\n case 'assets_set_genre': {\n const name = str(args.name)\n const description = str(args.description)\n if (name === '') throw new Error('assets_set_genre 需要 name')\n if (project.assets === undefined) project.assets = emptyProjectAssets()\n project.assets.genre = { name, description, children: [] }\n project.assets.updatedAt = new Date().toISOString()\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `题材已设为「${name}」`\n }\n case 'assets_set_progression': {\n const name = str(args.name)\n const driver = str(args.driver)\n const primary = args.primary !== false\n if (name === '') throw new Error('assets_set_progression 需要 name')\n if (project.assets === undefined) project.assets = emptyProjectAssets()\n const mode = {\n name,\n driver: driver !== '' ? driver : name,\n readerExpectation: str(args.readerExpectation),\n payoffs: Array.isArray(args.payoffs) ? args.payoffs.filter((v): v is string => typeof v === 'string') : [],\n risks: Array.isArray(args.risks) ? args.risks.filter((v): v is string => typeof v === 'string') : [],\n primary,\n }\n if (primary) project.assets.primaryProgression = mode\n else {\n if (project.assets.auxiliaryProgressions === undefined) project.assets.auxiliaryProgressions = []\n project.assets.auxiliaryProgressions.push(mode)\n }\n project.assets.updatedAt = new Date().toISOString()\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `推进模式${primary ? '(主)' : '(辅助)'}已设置:「${name}」`\n }\n case 'assets_add_rule': {\n const name = str(args.name)\n const avoid = str(args.avoid)\n if (avoid === '') throw new Error('assets_add_rule 需要 avoid(要避免的表达问题)')\n if (project.assets === undefined) project.assets = emptyProjectAssets()\n if (project.assets.antiAiRules === undefined) project.assets.antiAiRules = []\n project.assets.antiAiRules.push({\n name: name !== '' ? name : `自定义规则 ${project.assets.antiAiRules.length + 1}`,\n avoid,\n fix: str(args.fix),\n })\n project.assets.updatedAt = new Date().toISOString()\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `已新增反 AI 规则「${name !== '' ? name : avoid.slice(0, 20)}」`\n }\n default:\n throw new Error(`未知工具 ${name}`)\n }\n}\n\n// ------------------------------------------------------------------- chat\n\n/** Extract the first action directive from a reply (tolerant to common tag misspellings). */\nfunction extractAction(reply: string): { name: string; args: Record<string, unknown>; index: number } | undefined {\n // 容错:dsh-action 的常见误拼(dash-action / dsah-action / dhs-action 等)。\n const match = /<([a-z_]*d[a-z]?sh?-action)\\s+name=\"([^\"]+)\"\\s*>([\\s\\S]*?)<\\/\\1>/.exec(reply)\n if (match === null) return undefined\n const rawArgs = match[3]?.trim() ?? ''\n let args: Record<string, unknown>\n try {\n args = rawArgs === '' ? {} : JSON.parse(rawArgs) as Record<string, unknown>\n } catch {\n throw new Error(`动作参数不是合法 JSON:${rawArgs.slice(0, 80)}`)\n }\n return { name: match[2] ?? '', args, index: match.index }\n}\n\n/** Render the recent history as LLM messages (skipping tool chatter in early rounds). */\nfunction historyToMessages(history: AssistantMessage[]): Message[] {\n const recent = history.slice(-MAX_HISTORY_MESSAGES)\n const messages: Message[] = []\n for (const entry of recent) {\n if (entry.role === 'user') {\n messages.push(createUserMessage({\n content: [{ type: 'text', text: entry.content }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n }))\n } else if (entry.role === 'assistant') {\n messages.push(createAssistantMessage({\n content: [{ type: 'text', text: entry.content }],\n source: { provider: 'deepseek-official', model: 'deepseek-v4-flash' },\n }))\n } else if (entry.role === 'tool') {\n // 工具结果(book_overview/大纲全文等可能很大)截断后进上下文,\n // 避免多轮对话上下文翻倍膨胀触发「上下文到限制」。\n const body = entry.content.length > 2000\n ? entry.content.slice(0, 2000) + '\\n…(结果过长已截断,需要完整内容请重新调用工具)'\n : entry.content\n messages.push(createUserMessage({\n content: [{ type: 'text', text: `【工具 ${entry.tool ?? ''} 的执行结果】\\n${body}` }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n }))\n }\n }\n return messages\n}\n\n/** One non-streaming LLM chat turn (used inside the tool loop). */\nasync function chatOnce(\n ctx: Context,\n config: NovelConfig,\n system: string,\n history: AssistantMessage[],\n): Promise<string> {\n const messages = historyToMessages(history)\n const request: GenerateOptions = {\n provider: config.provider,\n model: config.model,\n messages,\n system,\n // v4-flash 推理模型:reasoning channel 占预算,给足避免回复被截断。\n maxTokens: Math.max(config.maxTokens, 16000),\n temperature: 0.7,\n }\n const assembler = new BlockAssembler()\n for await (const chunk of ctx.llm.stream(request)) {\n assembler.push(chunk)\n }\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n throw new Error(`助手调用失败(${finish.kind}): ${finish.failure.message}`)\n }\n const blocks = assembler.blocks()\n const textBlocks = blocks\n .filter((block): block is Extract<StreamChunk, { type: 'block-end' }>['block'] & { type: 'text' } => block.type === 'text')\n .map(block => block.text)\n let text = textBlocks.join('\\n').trim()\n if (text === '') {\n const reasoning = blocks\n .filter((block): block is { type: 'reasoning'; text: string } => block.type === 'reasoning')\n .map(block => block.text)\n .join('\\n')\n .trim()\n if (reasoning !== '') text = reasoning\n }\n return text\n}\n\n/** Run one user turn. Yields stream frames; persists history. */\nexport async function* runAssistantTurn(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n userMessage: string,\n): AsyncGenerator<\n | { frame: 'delta'; text: string }\n | { frame: 'tool'; name: string; status: 'start' | 'done' | 'error'; detail?: string }\n | { frame: 'toolDelta'; name: string; text: string },\n void,\n unknown\n> {\n const history = loadAssistantHistory(outputDir)\n const system = assistantSystemPrompt(project)\n\n // Persist the user message.\n const userEntry: AssistantMessage = { role: 'user', content: userMessage, ts: new Date().toISOString() }\n history.push(userEntry)\n appendHistory(outputDir, userEntry)\n\n let round = 0\n /** Whether we already nudged the model to emit an action tag (avoid loops). */\n let nudged = false\n for (;;) {\n const reply = await chatOnce(ctx, config, system, history)\n const action = extractAction(reply)\n\n if (action === undefined) {\n // No parseable action tag. If the reply clearly intends to modify\n // something (or contains a malformed action tag), nudge once.\n const intendsAction = /(改|修改|修订|重写|替换|调整|生成|新增|删除|导出|看看|查看|调出|读一下|加上|加一个|去掉|删掉|把.+改成)/.test(reply)\n const strayTag = /<[a-z_-]*action[^>]*>/.test(reply)\n if ((intendsAction || strayTag) && !nudged) {\n nudged = true\n const nudge = '你的上一条回复表达了想操作项目的意图(或动作标签格式有误),因此没有执行任何操作。请直接输出动作标签来执行,格式必须为 <dsh-action name=\"工具名\">{\"参数\":值}</dsh-action>(注意拼写是 dsh-action,不是 dash-action;标签成对出现,参数为合法 JSON)。如果需要先看内容,先输出 outline_text 或 chapter_text 标签。'\n history.push({ role: 'tool', content: nudge, tool: 'format-hint', ts: new Date().toISOString() })\n appendHistory(outputDir, { role: 'tool', content: nudge, tool: 'format-hint', ts: new Date().toISOString() })\n continue\n }\n // Plain prose reply — done.\n const assistantEntry: AssistantMessage = { role: 'assistant', content: reply, ts: new Date().toISOString() }\n history.push(assistantEntry)\n appendHistory(outputDir, assistantEntry)\n // Stream the prose (without any stray action markup).\n yield { frame: 'delta', text: reply }\n return\n }\n\n // Execute the action, then feed the result back and continue.\n const { name, args, index } = action\n const prose = reply.slice(0, index).trim()\n yield { frame: 'tool', name, status: 'start' }\n let result: string\n try {\n // executeAction is an async generator: it yields live progress text\n // (chapter text being generated) and returns the final result string.\n // Iterate manually so we see both the yielded deltas and the return.\n const iterator = executeAction(ctx, config, project, outputDir, name, args)[Symbol.asyncIterator]()\n result = ''\n for (;;) {\n const step = await iterator.next()\n if (step.done === true) {\n result = typeof step.value === 'string' ? step.value : ''\n break\n }\n const chunk = step.value\n if (typeof chunk === 'string' && chunk !== '') {\n yield { frame: 'toolDelta', name, text: chunk }\n }\n }\n yield { frame: 'tool', name, status: 'done', detail: result.slice(0, 200) }\n } catch (error) {\n result = `执行失败:${(error as Error).message}`\n yield { frame: 'tool', name, status: 'error', detail: (error as Error).message }\n }\n\n // Persist assistant prose + tool result as history entries.\n if (prose !== '') {\n history.push({ role: 'assistant', content: prose, ts: new Date().toISOString() })\n appendHistory(outputDir, { role: 'assistant', content: prose, ts: new Date().toISOString() })\n }\n history.push({ role: 'tool', content: result, tool: name, ts: new Date().toISOString() })\n appendHistory(outputDir, { role: 'tool', content: result, tool: name, ts: new Date().toISOString() })\n\n round++\n if (round >= MAX_TOOL_ROUNDS) {\n const message = `(已连续执行 ${round} 次修改操作,本轮停止。如需继续请再说。)`\n history.push({ role: 'assistant', content: message, ts: new Date().toISOString() })\n appendHistory(outputDir, { role: 'assistant', content: message, ts: new Date().toISOString() })\n yield { frame: 'delta', text: message }\n return\n }\n }\n}\n\nexport { chapterFileName }\n","/**\n * 书架(Bookshelf)— 多书管理:一本书记录一个独立输出目录。\n * 状态持久化到 ~/.dsh/dsh-novel-forge-bookshelf.json(跟随 dsh 配置惯例)。\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { randomBytes } from 'node:crypto'\nimport type { BookEntry, BookshelfSnapshot } from './protocol.ts'\nimport { loadProject } from './engine.ts'\n\n/** 书架配置文件路径。 */\nexport function bookshelfFile(): string {\n return join(homedir(), '.dsh', 'dsh-novel-forge-bookshelf.json')\n}\n\ninterface BookshelfStore {\n books: BookEntry[]\n activeBookId: string | null\n}\n\nfunction defaultStore(): BookshelfStore {\n return { books: [], activeBookId: null }\n}\n\n/** 读取书架(无则返回空)。 */\nexport function loadBookshelf(): BookshelfStore {\n const file = bookshelfFile()\n if (!existsSync(file)) return defaultStore()\n try {\n let raw = readFileSync(file, 'utf8')\n if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1)\n const parsed = JSON.parse(raw) as BookshelfStore\n if (!Array.isArray(parsed.books)) return defaultStore()\n return { books: parsed.books, activeBookId: parsed.activeBookId ?? null }\n } catch {\n return defaultStore()\n }\n}\n\n/** 持久化书架。 */\nfunction saveBookshelf(store: BookshelfStore): void {\n const file = bookshelfFile()\n mkdirSync(join(homedir(), '.dsh'), { recursive: true })\n writeFileSync(file, JSON.stringify(store, null, 2), 'utf8')\n}\n\n/** 当前激活的书。 */\nexport function activeBook(store: BookshelfStore): BookEntry | undefined {\n return store.books.find(b => b.id === store.activeBookId)\n}\n\n/** 书架快照(含每本书的进度摘要)。 */\nexport function bookshelfSnapshot(store: BookshelfStore): BookshelfSnapshot {\n return {\n books: store.books.map(book => {\n const project = loadProject(book.outputDir)\n const done = project === undefined ? 0 : project.chapters.filter(c => c.status === 'approved' || c.status === 'written' || c.status === 'rejected').length\n const hasCover = project?.coverPath !== undefined && project.coverPath !== '' && existsSync(join(book.outputDir, project.coverPath))\n return {\n ...book,\n done,\n total: project?.chapters.length ?? 0,\n hasProject: project !== undefined,\n hasCover,\n blurb: project?.blurb,\n }\n }),\n activeBookId: store.activeBookId,\n }\n}\n\n/** 新建一本书(自动成为当前书)。 */\nexport function createBook(bookName: string, outputDir: string): BookEntry {\n const store = loadBookshelf()\n const id = `book-${Date.now().toString(36)}-${randomBytes(3).toString('hex')}`\n const now = new Date().toISOString()\n const book: BookEntry = { id, bookName, outputDir, createdAt: now, updatedAt: now }\n store.books.push(book)\n store.activeBookId = id\n saveBookshelf(store)\n return book\n}\n\n/** 更新某本书的书名(开书向导导入大纲后书名以大纲首行为准)。 */\nexport function renameBook(id: string, bookName: string): boolean {\n const store = loadBookshelf()\n const book = store.books.find(b => b.id === id)\n if (book === undefined) return false\n book.bookName = bookName\n book.updatedAt = new Date().toISOString()\n saveBookshelf(store)\n return true\n}\n\n/**\n * 播种:书架为空时,把指定输出目录下已有的项目自动登记为第一本书。\n * 兼容升级场景 —— 旧版插件直接在输出目录写项目,从未登记书架。\n * @param outputDir - 候选输出目录(通常为 settings 的默认输出目录)。\n * @returns 是否发生了播种。\n */\nexport function seedBookshelfFromOutputDir(outputDir: string): boolean {\n const store = loadBookshelf()\n if (store.books.length > 0) return false\n if (!existsSync(outputDir)) return false\n // 有项目文件,或至少有章节文件,才视为\"已有的书\"。\n const hasProject = existsSync(join(outputDir, 'novel-project.json'))\n const hasChapters = existsSync(outputDir)\n if (!hasProject && !hasChapters) return false\n const project = loadProject(outputDir)\n const bookName = project?.bookName ?? outputDir.split(/[\\\\/]/).pop() ?? '未命名小说'\n createBook(bookName, outputDir)\n return true\n}\n\n/** 激活一本书。 */\nexport function activateBook(id: string): BookEntry | undefined {\n const store = loadBookshelf()\n const book = store.books.find(b => b.id === id)\n if (book === undefined) return undefined\n store.activeBookId = id\n book.updatedAt = new Date().toISOString()\n saveBookshelf(store)\n return book\n}\n\n/** 移除一本书。 */\nexport function removeBook(id: string): boolean {\n const store = loadBookshelf()\n const idx = store.books.findIndex(b => b.id === id)\n if (idx === -1) return false\n store.books.splice(idx, 1)\n if (store.activeBookId === id) {\n store.activeBookId = store.books[0]?.id ?? null\n }\n saveBookshelf(store)\n return true\n}\n\n/** 当前书输出目录(无书架则 undefined,回退 settings)。 */\nexport function activeBookOutputDir(): string | undefined {\n const book = activeBook(loadBookshelf())\n return book?.outputDir\n}\n\n/** 默认输出目录推断:桌面/书名。 */\nexport function defaultOutputDirFor(bookName: string): string {\n const clean = bookName.replace(/[\\\\/:*?\"<>|]/g, '').trim().slice(0, 40) || '未命名小说'\n return join(homedir(), 'Desktop', clean)\n}\n","/**\n * The /api/dsh-novel-forge route family: status, docx outline loading, LLM\n * story-bible extraction, volume planning, chapter planning, streaming\n * generation / rewrite / polish (NDJSON frames), review, summaries,\n * foreshadows, export, chapter reading, config patching, and opening the\n * output folder. Every route carries the same loopback-only trust fence as\n * the family plugins — these endpoints invoke the LLM and write files on the\n * host machine.\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { exec } from 'node:child_process'\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { WebRoute } from '@deepseek-ai/dsh-host-webserver'\nimport type { Context } from '@deepseek-ai/cordis'\nimport {\n NOVEL_API,\n type AssetsPatch,\n type AssetsResponse,\n type AssistantFrame,\n type AssistantHistoryResponse,\n type AssistantRequest,\n type AuditResponse,\n type BiblePatchRequest,\n type BibleRequest,\n type BlurbRequest,\n type CoverRequest,\n type CoverResponse,\n type RenameRequest,\n type WorldRequest,\n type BibleResponse,\n type BookActivateRequest,\n type BookCreateRequest,\n type BookRemoveRequest,\n type BookshelfSnapshot,\n type ChapterResponse,\n type ChapterPlan,\n type ChapterSaveResponse,\n type ChapterTextRequest,\n type ConfigPatch,\n type DraftDecisionRequest,\n type ExportRequest,\n type ExportResponse,\n type ForeshadowRequest,\n type ForeshadowResponse,\n type JobFrame,\n type LoadOutlineRequest,\n type LoadOutlineResponse,\n type NovelConfig,\n type PlanRequest,\n type PlanResponse,\n type PolishRequest,\n type ResetRequest,\n type ReviewReport,\n type ReviewRequest,\n type RewriteRequest,\n type StatusResponse,\n type StyleEngineRequest,\n type SummaryRequest,\n type VolumesRequest,\n type VolumesResponse,\n} from './protocol.ts'\nimport { readOutlineFromDocx } from './docx.ts'\nimport { clearAssistantHistory, loadAssistantHistory, runAssistantTurn } from './assistant.ts'\nimport { activateBook, bookshelfSnapshot, createBook, defaultOutputDirFor, loadBookshelf, removeBook, renameBook, seedBookshelfFromOutputDir } from './bookshelf.ts'\nimport { BUILTIN_ANTI_AI_RULES, BUILTIN_GENRE_LIBRARY, BUILTIN_PROGRESSION_MODES, BUILTIN_STYLE_TEMPLATES, emptyProjectAssets } from './assets.ts'\nimport {\n chapterFileName,\n auditBook,\n backfillFacts,\n createProject,\n exportBook,\n extractBible,\n extractStyleAsset,\n extractWorld,\n generateBlurb,\n generateChapterStream,\n listChapterFiles,\n loadProject,\n planChapters,\n planVolumes,\n polishChapterStream,\n readChapterFile,\n refreshCharacters,\n reviewChapter,\n reviewChapterText,\n rewriteChapterStream,\n saveProject,\n suggestForeshadows,\n summarizeAndExtractFacts,\n summarizeChapter,\n syncProjectWithDisk,\n} from './engine.ts'\n\n/** Cap on JSON request bodies (generous: cover images travel as base64). */\nconst MAX_JSON_BODY_BYTES = 16 * 1024 * 1024\n\n/** Loopback-only fence (mirrors the family plugins' pairing routes). */\nfunction isLoopbackRequest(request: IncomingMessage): boolean {\n const address = request.socket.remoteAddress\n if (address !== '127.0.0.1' && address !== '::1' && address !== '::ffff:127.0.0.1') return false\n const host = request.headers.host\n if (typeof host !== 'string') return false\n let hostUrl: URL\n try {\n hostUrl = new URL(`http://${host}`)\n } catch {\n return false\n }\n if (hostUrl.hostname !== '127.0.0.1' && hostUrl.hostname !== 'localhost' && hostUrl.hostname !== '[::1]') return false\n if (request.headers['sec-fetch-site'] === 'cross-site') return false\n const origin = request.headers.origin\n if (origin === undefined) return true\n try {\n return new URL(origin).host === hostUrl.host\n } catch {\n return false\n }\n}\n\n/** One JSON response. */\nfunction writeJson(res: ServerResponse, status: number, body: unknown): void {\n const payload = JSON.stringify(body)\n res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'referrer-policy': 'no-referrer' })\n res.end(payload)\n}\n\n/** Read a JSON request body. */\nasync function readJsonBody<T>(req: IncomingMessage): Promise<T | undefined> {\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of req) {\n const buffer = chunk as Buffer\n size += buffer.length\n if (size > MAX_JSON_BODY_BYTES) return undefined\n chunks.push(buffer)\n }\n try {\n return JSON.parse(Buffer.concat(chunks).toString('utf8')) as T\n } catch {\n return undefined\n }\n}\n\n/** Route deps. */\nexport interface NovelRoutesDeps {\n ctx: Context\n /** Resolve the live plugin config (settings-aware). */\n getConfig: () => NovelConfig\n /** Persist a config patch through the settings seam. */\n patchConfig: (patch: ConfigPatch) => Promise<NovelConfig>\n}\n\n/** Default chapter count for planning when the request omits it. */\nconst DEFAULT_PLAN_COUNT = 30\n\n/**\n * Build every /api/dsh-novel-forge route.\n * @param deps - context, config resolver, config patcher.\n * @returns the route list.\n */\nexport function makeRoutes(deps: NovelRoutesDeps): WebRoute[] {\n const { ctx, getConfig, patchConfig } = deps\n\n /** Guard helper: fence + method check. */\n const guard = (req: IncomingMessage, res: ServerResponse, method: string): boolean => {\n if (!isLoopbackRequest(req)) {\n writeJson(res, 403, { error: 'forbidden: loopback-only' })\n return false\n }\n if (req.method !== method) {\n writeJson(res, 405, { error: `method not allowed (expected ${method})` })\n return false\n }\n return true\n }\n\n /** Load (and sync) the project, or respond 400. */\n const requireProject = (res: ServerResponse): ReturnType<typeof loadProject> => {\n const config = getConfig()\n const project = loadProject(config.outputDir)\n if (project === undefined) {\n writeJson(res, 400, { error: '输出目录中没有项目,请先加载大纲' })\n return undefined\n }\n syncProjectWithDisk(project, config.outputDir)\n saveProject(config.outputDir, project)\n return project\n }\n\n // -------------------------------------------------------------- status\n const statusRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.status,\n handler: (req, res) => {\n if (!guard(req, res, 'GET')) return\n const config = getConfig()\n // 书架为空时播种 settings 默认输出目录里的已有项目。\n seedBookshelfFromOutputDir(config.outputDir)\n const project = loadProject(config.outputDir)\n if (project !== undefined) {\n syncProjectWithDisk(project, config.outputDir)\n saveProject(config.outputDir, project)\n }\n const response: StatusResponse = {\n config,\n // 瘦身:facts 只回最近 80 条(客户端最多展示 60),避免长篇后\n // status 响应体随编年录无限膨胀。\n project: project !== undefined\n ? { ...project, facts: (project.facts ?? []).slice(-80) }\n : undefined,\n generatedFiles: listChapterFiles(config.outputDir),\n }\n writeJson(res, 200, response)\n },\n }\n\n // -------------------------------------------------------- load-outline\n const loadOutlineRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.loadOutline,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<LoadOutlineRequest>(req)\n const config = getConfig()\n try {\n let outline: string\n let path: string | undefined\n if (body?.text !== undefined && body.text.trim() !== '') {\n outline = body.text.trim()\n } else {\n const target = body?.path?.trim() !== '' && body?.path !== undefined ? body.path : config.outlinePath\n outline = readOutlineFromDocx(target)\n path = target\n }\n if (outline.length < 50) {\n writeJson(res, 400, { error: '大纲内容过短(<50 字符),请检查文件或直接粘贴大纲文本' })\n return\n }\n const response: LoadOutlineResponse = {\n outline,\n bookName: createProject(outline).bookName,\n chars: outline.length,\n path,\n }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 400, { error: (error as Error).message })\n }\n },\n }\n\n // -------------------------------------------------------- save-outline\n const saveOutlineRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.saveOutline,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<LoadOutlineRequest>(req)\n const config = getConfig()\n const outline = body?.text ?? ''\n if (outline.trim().length < 50) {\n writeJson(res, 400, { error: '大纲内容过短(<50 字符)' })\n return\n }\n let project = loadProject(config.outputDir)\n const now = new Date().toISOString()\n if (project === undefined) {\n project = createProject(outline)\n } else {\n project.outline = outline\n project.bookName = createProject(outline).bookName\n project.updatedAt = now\n }\n saveProject(config.outputDir, project)\n writeJson(res, 200, { ok: true, bookName: project.bookName })\n },\n }\n\n // --------------------------------------------------------------- bible\n const bibleRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.bible,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<BibleRequest>(req)\n const config = getConfig()\n const project = loadProject(config.outputDir)\n const outline = body?.outline?.trim() !== '' && body?.outline !== undefined\n ? body.outline\n : project?.outline\n if (outline === undefined || outline.length < 50) {\n writeJson(res, 400, { error: '请先加载大纲' })\n return\n }\n try {\n const bible = await extractBible(ctx, config, outline)\n const now = new Date().toISOString()\n const next = project ?? createProject(outline)\n next.bible = bible\n next.updatedAt = now\n saveProject(config.outputDir, next)\n const response: BibleResponse = { bible }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ------------------------------------------------------------- volumes\n const volumesRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.volumes,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<VolumesRequest>(req)\n const config = getConfig()\n const project = loadProject(config.outputDir)\n const outline = body?.outline?.trim() !== '' && body?.outline !== undefined\n ? body.outline\n : project?.outline\n if (outline === undefined || outline.length < 50) {\n writeJson(res, 400, { error: '请先加载大纲' })\n return\n }\n try {\n const volumes = await planVolumes(ctx, config, outline)\n const now = new Date().toISOString()\n const next = project ?? createProject(outline)\n next.volumes = volumes\n next.updatedAt = now\n saveProject(config.outputDir, next)\n const response: VolumesResponse = { volumes }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ----------------------------------------------------------------- plan\n const planRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.plan,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<PlanRequest>(req)\n const config = getConfig()\n const project = loadProject(config.outputDir)\n const outline = body?.outline?.trim() !== '' && body?.outline !== undefined\n ? body.outline\n : project?.outline\n if (outline === undefined || outline.length < 50) {\n writeJson(res, 400, { error: '请先加载大纲(或粘贴大纲文本)' })\n return\n }\n const count = body?.chapterCount ?? DEFAULT_PLAN_COUNT\n if (!Number.isInteger(count) || count < 1 || count > 200) {\n writeJson(res, 400, { error: 'chapterCount 须为 1-200 的整数' })\n return\n }\n try {\n const next = project ?? createProject(outline)\n const chapters: ChapterPlan[] = await planChapters(ctx, config, next, count, body?.volume)\n next.chapters.push(...chapters)\n next.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, next)\n const response: PlanResponse = { chapters, volumes: next.volumes }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ------------------------------------------------------------- generate\n const generateRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.generate,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<{ chapterNo?: number; skipReview?: boolean }>(req)\n const rawNo = body?.chapterNo\n if (!Number.isInteger(rawNo) || rawNo === undefined || rawNo < 1) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const no: number = rawNo\n const chapter = project.chapters.find(c => c.no === no)\n if (chapter === undefined) {\n writeJson(res, 404, { error: `章节 ${no} 不在计划中` })\n return\n }\n if (chapter.status === 'generating') {\n writeJson(res, 409, { error: `章节 ${no} 正在生成中` })\n return\n }\n\n // NDJSON stream.\n res.writeHead(200, {\n 'content-type': 'application/x-ndjson; charset=utf-8',\n 'cache-control': 'no-cache',\n 'x-accel-buffering': 'no',\n 'referrer-policy': 'no-referrer',\n })\n chapter.status = 'generating'\n chapter.error = undefined\n saveProject(config.outputDir, project)\n\n const send = (frame: JobFrame): void => {\n res.write(JSON.stringify(frame) + '\\n')\n }\n\n try {\n send({ type: 'start', no, title: chapter.title })\n for await (const step of generateChapterStream(ctx, config, project, config.outputDir, no)) {\n if (step.frame === 'delta') {\n send({ type: 'delta', text: step.text })\n } else if (step.frame === 'done') {\n send({ type: 'done', no, file: step.file, chars: step.chars, title: chapter.title })\n }\n }\n // Auto pipeline: summary + facts(一次调用)-> review(unless skipped)。\n try {\n await summarizeAndExtractFacts(ctx, config, project, config.outputDir, no)\n } catch (error) {\n console.warn('[dsh-novel-forge] summary/facts failed:', (error as Error).message)\n }\n if (!(body?.skipReview === true) && (config.autoReview ?? true)) {\n const report = await reviewChapter(ctx, config, project, config.outputDir, no)\n send({ type: 'review', no, report })\n } else {\n chapter.status = 'approved'\n saveProject(config.outputDir, project)\n }\n res.end()\n } catch (error) {\n chapter.status = 'error'\n chapter.error = (error as Error).message\n saveProject(config.outputDir, project)\n if (!res.writableEnded) {\n send({ type: 'error', no, message: (error as Error).message })\n res.end()\n }\n }\n },\n }\n\n // --------------------------------------------------------------- review\n const reviewRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.review,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<ReviewRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const no = body!.chapterNo!\n try {\n const report = await reviewChapter(ctx, config, project, config.outputDir, no)\n writeJson(res, 200, { report })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // -------------------------------------------------------------- rewrite\n const rewriteRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.rewrite,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<RewriteRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const no = body!.chapterNo!\n res.writeHead(200, {\n 'content-type': 'application/x-ndjson; charset=utf-8',\n 'cache-control': 'no-cache',\n 'x-accel-buffering': 'no',\n 'referrer-policy': 'no-referrer',\n })\n const send = (frame: JobFrame): void => { res.write(JSON.stringify(frame) + '\\n') }\n try {\n for await (const step of rewriteChapterStream(ctx, config, project, config.outputDir, no, body?.instructions ?? '', body?.target)) {\n if (step.frame === 'delta') send({ type: 'delta', text: step.text })\n else if (step.frame === 'drafted') send({ type: 'drafted', no, chars: step.chars, draft: step.draft })\n }\n // Draft mode: no auto re-review — the user reviews the diff and\n // decides; re-run review after applying if wanted.\n res.end()\n } catch (error) {\n if (!res.writableEnded) {\n send({ type: 'error', no, message: (error as Error).message })\n res.end()\n }\n }\n },\n }\n\n // --------------------------------------------------------------- polish\n const polishRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.polish,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<PolishRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const no = body!.chapterNo!\n res.writeHead(200, {\n 'content-type': 'application/x-ndjson; charset=utf-8',\n 'cache-control': 'no-cache',\n 'x-accel-buffering': 'no',\n 'referrer-policy': 'no-referrer',\n })\n const send = (frame: JobFrame): void => { res.write(JSON.stringify(frame) + '\\n') }\n try {\n for await (const step of polishChapterStream(ctx, config, project, config.outputDir, no)) {\n if (step.frame === 'delta') send({ type: 'delta', text: step.text })\n else if (step.frame === 'drafted') send({ type: 'drafted', no, chars: step.chars, draft: step.draft })\n }\n res.end()\n } catch (error) {\n if (!res.writableEnded) {\n send({ type: 'error', no, message: (error as Error).message })\n res.end()\n }\n }\n },\n }\n\n // ---------------------------------------------------- draft apply/discard\n /** 采纳待确认草稿:覆盖正文文件 + 状态回 written + 清空草稿。 */\n const draftApplyRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.draftApply,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<DraftDecisionRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const chapter = project.chapters.find(c => c.no === body!.chapterNo!)\n if (chapter === undefined) {\n writeJson(res, 404, { error: `章节 ${body!.chapterNo} 不在计划中` })\n return\n }\n if (chapter.pendingDraft === undefined || chapter.pendingDraft === '') {\n writeJson(res, 400, { error: `章节 ${chapter.no} 没有待确认的草稿` })\n return\n }\n const draft = chapter.pendingDraft\n const fileName = chapterFileName(chapter)\n mkdirSync(config.outputDir, { recursive: true })\n const targetPath = join(config.outputDir, fileName)\n // 采纳前自动备份当前原稿为 .bak.md(每次应用都刷新为最新原稿),可随时回退。\n if (existsSync(targetPath)) {\n copyFileSync(targetPath, join(config.outputDir, `${fileName.replace(/\\.md$/, '')}.bak.md`))\n }\n writeFileSync(targetPath, `# 第${chapter.no}章 ${chapter.title}\\n\\n${draft}\\n`, 'utf8')\n chapter.pendingDraft = undefined\n chapter.status = 'written'\n chapter.chars = draft.length\n chapter.file = fileName\n chapter.review = undefined\n chapter.error = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { ok: true, chars: draft.length, file: fileName })\n },\n }\n\n /** 放弃待确认草稿:保留原稿,仅清空草稿字段。 */\n const draftDiscardRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.draftDiscard,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<DraftDecisionRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const chapter = project.chapters.find(c => c.no === body!.chapterNo!)\n if (chapter === undefined) {\n writeJson(res, 404, { error: `章节 ${body!.chapterNo} 不在计划中` })\n return\n }\n if (chapter.pendingDraft !== undefined) {\n chapter.pendingDraft = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n }\n writeJson(res, 200, { ok: true })\n },\n }\n\n // -------------------------------------------------------------- summary\n const summaryRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.summary,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<SummaryRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n try {\n const summary = await summarizeChapter(ctx, config, project, config.outputDir, body!.chapterNo!)\n writeJson(res, 200, { summary })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ---------------------------------------------------------- foreshadow\n const foreshadowRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.foreshadow,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<ForeshadowRequest>(req)\n try {\n if (body?.suggest === true) {\n // AI suggestion pass: create several foreshadows from the outline.\n const created = await suggestForeshadows(ctx, config, project)\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n const response: ForeshadowResponse = { foreshadows: created }\n writeJson(res, 200, response)\n return\n }\n if (body?.id !== undefined) {\n // Update an existing foreshadow.\n const target = project.foreshadows.find(f => f.id === body.id)\n if (target === undefined) {\n writeJson(res, 404, { error: `伏笔 ${body.id} 不存在` })\n return\n }\n if (body.description !== undefined) target.description = body.description\n if (body.plantedChapter !== undefined) target.plantedChapter = body.plantedChapter\n if (body.targetChapter !== undefined) target.targetChapter = body.targetChapter\n if (body.status !== undefined) target.status = body.status\n if (body.resolvedNote !== undefined) target.resolvedNote = body.resolvedNote\n } else {\n // Create one manually.\n const description = body?.description?.trim()\n if (description === undefined || description === '') {\n writeJson(res, 400, { error: 'description 必填' })\n return\n }\n project.foreshadows.push({\n id: `fs-${Date.now().toString(36)}`,\n description,\n plantedChapter: body?.plantedChapter,\n targetChapter: body?.targetChapter,\n status: body?.status ?? 'planned',\n })\n }\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n const response: ForeshadowResponse = { foreshadows: project.foreshadows }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // -------------------------------------------------------------- export\n const exportRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.exportBook,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<ExportRequest>(req)\n const format = body?.format === 'md' ? 'md' : 'txt'\n try {\n const result = exportBook(config.outputDir, project, format)\n const response: ExportResponse = { ...result }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // -------------------------------------------------------------- chapter\n const chapterRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.chapter,\n handler: async (req, res) => {\n if (!guard(req, res, 'GET')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const url = new URL(req.url ?? '/', 'http://localhost')\n const rawNo = Number(url.searchParams.get('no') ?? '0')\n if (!Number.isInteger(rawNo) || rawNo < 1) {\n writeJson(res, 400, { error: 'no 须为正整数' })\n return\n }\n const chapter = project.chapters.find(c => c.no === rawNo)\n if (chapter === undefined) {\n writeJson(res, 404, { error: `章节 ${rawNo} 不在计划中` })\n return\n }\n const markdown = readChapterFile(config.outputDir, chapter)\n if (markdown === undefined) {\n writeJson(res, 404, { error: `章节 ${rawNo} 尚未生成` })\n return\n }\n const response: ChapterResponse = { no: chapter.no, title: chapter.title, markdown }\n writeJson(res, 200, response)\n },\n }\n\n // ------------------------------------------------------ chapter check/save\n /** 审查手动编辑的正文(不落盘,返回审稿报告)。 */\n const chapterCheckRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.chapterCheck,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<ChapterTextRequest>(req)\n const text = body?.text?.trim() ?? ''\n if (text.length < 50) {\n writeJson(res, 400, { error: '正文过短(<50 字),请先编辑内容' })\n return\n }\n try {\n const report = await reviewChapterText(ctx, config, project, text)\n writeJson(res, 200, { report })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n /** 保存手动编辑的正文(自动备份 .bak,状态回 written)。 */\n const chapterSaveRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.chapterSave,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<ChapterTextRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const chapter = project.chapters.find(c => c.no === body!.chapterNo!)\n if (chapter === undefined) {\n writeJson(res, 404, { error: `章节 ${body!.chapterNo} 不在计划中` })\n return\n }\n const text = body?.text?.trim() ?? ''\n if (text.length < 50) {\n writeJson(res, 400, { error: '正文过短(<50 字),未保存' })\n return\n }\n const fileName = chapterFileName(chapter)\n mkdirSync(config.outputDir, { recursive: true })\n const targetPath = join(config.outputDir, fileName)\n if (existsSync(targetPath)) {\n copyFileSync(targetPath, join(config.outputDir, `${fileName.replace(/\\.md$/, '')}.bak.md`))\n }\n writeFileSync(targetPath, `# 第${chapter.no}章 ${chapter.title}\\n\\n${text}\\n`, 'utf8')\n chapter.status = 'written'\n chapter.chars = text.length\n chapter.file = fileName\n chapter.pendingDraft = undefined\n // 保存即审稿:携带工作区审查报告则沿用(不重复审);否则自动正式审稿一次。\n let report: ReviewReport | undefined\n const carried = body?.report\n if (carried !== undefined && typeof carried.score === 'number') {\n report = carried\n chapter.review = report\n chapter.status = report.passed ? 'approved' : 'rejected'\n } else {\n report = await reviewChapter(ctx, config, project, config.outputDir, chapter.no)\n }\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n const response: ChapterSaveResponse = { ok: true, chars: text.length, file: fileName, report }\n writeJson(res, 200, response)\n },\n }\n\n // ----------------------------------------------------------- assistant\n const assistantRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.assistant,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<AssistantRequest>(req)\n const message = body?.message?.trim()\n if (message === undefined || message === '') {\n writeJson(res, 400, { error: '消息不能为空' })\n return\n }\n res.writeHead(200, {\n 'content-type': 'application/x-ndjson; charset=utf-8',\n 'cache-control': 'no-cache',\n 'x-accel-buffering': 'no',\n 'referrer-policy': 'no-referrer',\n })\n const send = (frame: AssistantFrame): void => { res.write(JSON.stringify(frame) + '\\n') }\n try {\n for await (const step of runAssistantTurn(ctx, config, project, config.outputDir, message)) {\n if (step.frame === 'delta') send({ type: 'delta', text: step.text })\n else if (step.frame === 'tool') send({ type: 'tool', name: step.name, status: step.status, detail: step.detail })\n else if (step.frame === 'toolDelta') send({ type: 'toolDelta', name: step.name, text: step.text })\n }\n send({ type: 'done' })\n res.end()\n } catch (error) {\n if (!res.writableEnded) {\n send({ type: 'error', message: (error as Error).message })\n res.end()\n }\n }\n },\n }\n\n // -------------------------------------------------- assistant-history\n const assistantHistoryRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.assistantHistory,\n handler: (req, res) => {\n if (!guard(req, res, 'GET')) return\n const config = getConfig()\n const response: AssistantHistoryResponse = { messages: loadAssistantHistory(config.outputDir) }\n writeJson(res, 200, response)\n },\n }\n\n // --------------------------------------------------- assistant-clear\n /** 清空助手对话记录。 */\n const assistantClearRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.assistantClear,\n handler: (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n clearAssistantHistory(config.outputDir)\n writeJson(res, 200, { ok: true })\n },\n }\n\n // --------------------------------------------------------------- assets\n const assetsRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.assets,\n handler: async (req, res) => {\n // GET (read) and POST (patch) are both allowed — check methods first,\n // then the loopback fence (guard() would 405 on POST, which is wrong).\n if (req.method !== 'GET' && req.method !== 'POST') {\n writeJson(res, 405, { error: 'method not allowed (expected GET or POST)' })\n return\n }\n if (!isLoopbackRequest(req)) {\n writeJson(res, 403, { error: 'forbidden: loopback-only' })\n return\n }\n const config = getConfig()\n const project = loadProject(config.outputDir)\n const projectAssets = project?.assets ?? emptyProjectAssets()\n if (req.method === 'POST') {\n const body = await readJsonBody<AssetsPatch>(req)\n if (body === undefined) {\n writeJson(res, 400, { error: '无效的 JSON' })\n return\n }\n if (project === undefined) {\n writeJson(res, 400, { error: '请先加载大纲创建项目' })\n return\n }\n if (body.genre !== undefined) projectAssets.genre = body.genre\n if (body.primaryProgression !== undefined) projectAssets.primaryProgression = body.primaryProgression\n if (body.auxiliaryProgressions !== undefined) projectAssets.auxiliaryProgressions = body.auxiliaryProgressions\n if (body.antiAiRules !== undefined) projectAssets.antiAiRules = body.antiAiRules\n if (body.styleAssets !== undefined) projectAssets.styleAssets = body.styleAssets\n projectAssets.updatedAt = new Date().toISOString()\n project.assets = projectAssets\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n }\n const response: AssetsResponse = {\n projectAssets,\n genreLibrary: BUILTIN_GENRE_LIBRARY,\n antiAiLibrary: BUILTIN_ANTI_AI_RULES,\n styleTemplates: BUILTIN_STYLE_TEMPLATES,\n progressionLibrary: BUILTIN_PROGRESSION_MODES,\n }\n writeJson(res, 200, response)\n },\n }\n\n // ----------------------------------------------------------- style-engine\n const styleEngineRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.styleEngine,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = loadProject(config.outputDir)\n const body = await readJsonBody<StyleEngineRequest>(req)\n const sample = body?.sampleText?.trim()\n if (sample === undefined || sample.length < 50) {\n writeJson(res, 400, { error: '样本文本过短(<50 字符),请粘贴一段能代表目标风格的文字' })\n return\n }\n try {\n const rules = await extractStyleAsset(ctx, config, sample)\n const name = body?.name?.trim() !== '' && body?.name !== undefined ? body.name : `风格资产 ${Date.now().toString(36)}`\n const styleAsset = {\n name: name.slice(0, 40),\n ...rules,\n sourceText: sample.slice(0, 3000),\n createdAt: new Date().toISOString(),\n }\n if (project !== undefined) {\n project.assets ??= emptyProjectAssets()\n project.assets.styleAssets ??= []\n project.assets.styleAssets.push(styleAsset)\n project.assets.updatedAt = new Date().toISOString()\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n }\n writeJson(res, 200, { styleAsset })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ------------------------------------------------------------- bookshelf\n const bookshelfRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.bookshelf,\n handler: async (req, res) => {\n // GET = snapshot; POST = create book.\n if (req.method === 'GET') {\n if (!isLoopbackRequest(req)) {\n writeJson(res, 403, { error: 'forbidden: loopback-only' })\n return\n }\n // 书架为空时,把 settings 默认输出目录里已有的项目播种为第一本书。\n seedBookshelfFromOutputDir(getConfig().outputDir)\n const snapshot: BookshelfSnapshot = bookshelfSnapshot(loadBookshelf())\n writeJson(res, 200, snapshot)\n return\n }\n if (req.method === 'POST') {\n if (!isLoopbackRequest(req)) {\n writeJson(res, 403, { error: 'forbidden: loopback-only' })\n return\n }\n const body = await readJsonBody<BookCreateRequest>(req)\n const bookName = body?.bookName?.trim()\n if (bookName === undefined || bookName === '') {\n writeJson(res, 400, { error: 'bookName 不能为空' })\n return\n }\n const outputDir = body?.outputDir?.trim() !== '' && body?.outputDir !== undefined\n ? body.outputDir\n : defaultOutputDirFor(bookName)\n const book = createBook(bookName, outputDir)\n // 开书向导:创建时带大纲 → 立即建立项目(书名以大纲首行为准)。\n const outline = body?.outline?.trim()\n if (outline !== undefined && outline.length >= 50) {\n const project = createProject(outline)\n saveProject(outputDir, project)\n renameBook(book.id, project.bookName)\n }\n writeJson(res, 200, bookshelfSnapshot(loadBookshelf()))\n return\n }\n writeJson(res, 405, { error: 'method not allowed (expected GET or POST)' })\n },\n }\n\n // --------------------------------------------------------------- reset\n /** 重置项目:清空设定/卷/章节计划/正文/伏笔/资产/事实库(可携带新大纲)。 */\n const resetRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.reset,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = loadProject(config.outputDir)\n if (project === undefined) {\n writeJson(res, 400, { error: '输出目录中没有项目,无需重置' })\n return\n }\n const body = await readJsonBody<ResetRequest>(req)\n const outline = body?.outline?.trim()\n if (outline !== undefined && outline.length >= 50) {\n project.outline = outline\n project.bookName = createProject(outline).bookName\n }\n project.bible = undefined\n project.volumes = undefined\n project.chapters = []\n project.foreshadows = []\n project.assets = emptyProjectAssets()\n project.facts = []\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { ok: true, bookName: project.bookName })\n },\n }\n\n // --------------------------------------------------- bookshelf activate\n const bookshelfActivateRoute: WebRoute = {\n kind: 'exact',\n path: '/api/dsh-novel-forge/bookshelf/activate',\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<BookActivateRequest>(req)\n if (body?.id === undefined || body.id === '') {\n writeJson(res, 400, { error: 'id 不能为空' })\n return\n }\n const book = activateBook(body.id)\n if (book === undefined) {\n writeJson(res, 404, { error: `书 ${body.id} 不存在` })\n return\n }\n writeJson(res, 200, bookshelfSnapshot(loadBookshelf()))\n },\n }\n\n // ---------------------------------------------------- bookshelf remove\n const bookshelfRemoveRoute: WebRoute = {\n kind: 'exact',\n path: '/api/dsh-novel-forge/bookshelf/remove',\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<BookRemoveRequest>(req)\n if (body?.id === undefined || body.id === '') {\n writeJson(res, 400, { error: 'id 不能为空' })\n return\n }\n const removed = removeBook(body.id)\n if (!removed) {\n writeJson(res, 404, { error: `书 ${body.id} 不存在` })\n return\n }\n writeJson(res, 200, bookshelfSnapshot(loadBookshelf()))\n },\n }\n\n // ---------------------------------------------------------------- audit\n /** 全书一致性质检。 */\n const auditRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.audit,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n try {\n const issues = await auditBook(ctx, config, project, config.outputDir)\n const response: AuditResponse = {\n issues,\n auditedChapters: project.chapters.filter(c => c.status !== 'pending' && c.status !== 'generating').length,\n auditedAt: new Date().toISOString(),\n }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ----------------------------------------------------- characters refresh\n /** 角色卡刷新(出场统计精确化 + LLM 聚合状态)。 */\n const charactersRefreshRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.charactersRefresh,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n try {\n const cards = await refreshCharacters(ctx, config, project, config.outputDir)\n writeJson(res, 200, { cards })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ------------------------------------------------------- facts backfill\n /** 事实库回填:对历史已生成章节批量抽取事实。 */\n const factsBackfillRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.factsBackfill,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n try {\n const filled = await backfillFacts(ctx, config, project, config.outputDir)\n writeJson(res, 200, { ok: true, filled })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ---------------------------------------------------------- bible patch\n /** 设定圣经局部修补(世界观规则/红线/风格)。 */\n const biblePatchRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.biblePatch,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n if (project.bible === undefined) {\n writeJson(res, 400, { error: '尚未生成道藏,请先生成' })\n return\n }\n const body = await readJsonBody<BiblePatchRequest>(req)\n if (Array.isArray(body?.worldRules)) project.bible.worldRules = body.worldRules.filter(r => r.trim() !== '')\n if (Array.isArray(body?.redLines)) project.bible.redLines = body.redLines.filter(r => r.trim() !== '')\n if (Array.isArray(body?.style)) project.bible.style = body.style.filter(r => r.trim() !== '')\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { bible: project.bible })\n },\n }\n\n // ---------------------------------------------------------------- blurb\n /** 小说简介:AI 生成/补全,或手动保存。 */\n const blurbRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.blurb,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<BlurbRequest>(req)\n try {\n if (body?.action === 'save') {\n const text = body.text?.trim() ?? ''\n project.blurb = text\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { blurb: text })\n return\n }\n const partial = body?.partial?.trim() ?? ''\n const blurb = await generateBlurb(ctx, config, project, partial)\n project.blurb = blurb\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { blurb })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ---------------------------------------------------------------- cover\n /** 封面:GET 读取(dataUrl);POST 上传(base64)或移除。 */\n const coverRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.cover,\n handler: async (req, res) => {\n const config = getConfig()\n if (req.method === 'GET') {\n if (!isLoopbackRequest(req)) {\n writeJson(res, 403, { error: 'forbidden: loopback-only' })\n return\n }\n // 书架页按书读取:?dir=<输出目录>;省略时用当前激活书目录。\n const url = new URL(req.url ?? '/', 'http://localhost')\n const dirParam = url.searchParams.get('dir')\n const targetDir = dirParam !== null && dirParam !== '' ? dirParam : config.outputDir\n const project = loadProject(targetDir)\n const coverPath = project?.coverPath\n if (coverPath === undefined || coverPath === '') {\n writeJson(res, 200, { dataUrl: null } satisfies CoverResponse)\n return\n }\n const file = join(targetDir, coverPath)\n if (!existsSync(file)) {\n writeJson(res, 200, { dataUrl: null } satisfies CoverResponse)\n return\n }\n const mime = coverPath.toLowerCase().endsWith('.png') ? 'image/png'\n : coverPath.toLowerCase().endsWith('.jpg') || coverPath.toLowerCase().endsWith('.jpeg') ? 'image/jpeg'\n : coverPath.toLowerCase().endsWith('.webp') ? 'image/webp'\n : 'image/png'\n const dataUrl = `data:${mime};base64,${readFileSync(file).toString('base64')}`\n writeJson(res, 200, { dataUrl } satisfies CoverResponse)\n return\n }\n if (req.method === 'POST') {\n if (!guard(req, res, 'POST')) return\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<CoverRequest>(req)\n try {\n if (body?.action === 'remove') {\n if (project.coverPath !== undefined && project.coverPath !== '') {\n const oldFile = join(config.outputDir, project.coverPath)\n if (existsSync(oldFile)) rmSync(oldFile, { force: true })\n }\n project.coverPath = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { ok: true, coverPath: null })\n return\n }\n const dataUrl = body?.dataUrl ?? ''\n const match = /^data:(image\\/(?:png|jpeg|jpg|webp));base64,(.+)$/s.exec(dataUrl)\n if (match === null) {\n writeJson(res, 400, { error: '封面须为 PNG/JPEG/WebP 的 base64 data URL' })\n return\n }\n const mime = match[1]!\n const ext = mime === 'image/png' ? 'png' : mime === 'image/webp' ? 'webp' : 'jpg'\n const fileName = `cover.${ext}`\n const targetPath = join(config.outputDir, fileName)\n const oldPath = project.coverPath\n mkdirSync(config.outputDir, { recursive: true })\n writeFileSync(targetPath, Buffer.from(match[2]!, 'base64'))\n if (oldPath !== undefined && oldPath !== '' && oldPath !== fileName) {\n const oldFile = join(config.outputDir, oldPath)\n if (existsSync(oldFile)) rmSync(oldFile, { force: true })\n }\n project.coverPath = fileName\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { ok: true, coverPath: fileName })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n return\n }\n writeJson(res, 405, { error: 'method not allowed (expected GET or POST)' })\n },\n }\n\n // ---------------------------------------------------------------- world\n /** 大世界:AI 提炼或手动保存(境界体系/区域/势力)。 */\n const worldRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.world,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<WorldRequest>(req)\n try {\n if (body?.action === 'save' && body.world !== undefined) {\n project.world = {\n realms: Array.isArray(body.world.realms) ? body.world.realms : [],\n regions: Array.isArray(body.world.regions) ? body.world.regions : [],\n factions: Array.isArray(body.world.factions) ? body.world.factions : [],\n }\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { world: project.world })\n return\n }\n const world = await extractWorld(ctx, config, project)\n project.world = world\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { world })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // --------------------------------------------------------------- rename\n /** 重命名当前书:同步项目 bookName 与书架条目。 */\n const renameRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.rename,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<RenameRequest>(req)\n const bookName = body?.bookName?.trim()\n if (bookName === undefined || bookName === '') {\n writeJson(res, 400, { error: '书名不能为空' })\n return\n }\n project.bookName = bookName.slice(0, 60)\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n const store = loadBookshelf()\n if (store.activeBookId !== null) renameBook(store.activeBookId, project.bookName)\n writeJson(res, 200, { bookName: project.bookName })\n },\n }\n\n // --------------------------------------------------------------- config\n const configRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.config,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<ConfigPatch>(req)\n if (body === undefined) {\n writeJson(res, 400, { error: '无效的配置 JSON' })\n return\n }\n try {\n const next = await patchConfig(body)\n writeJson(res, 200, { config: next })\n } catch (error) {\n writeJson(res, 400, { error: (error as Error).message })\n }\n },\n }\n\n // ---------------------------------------------------------- open-folder\n const openFolderRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.openFolder,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const dir = config.outputDir\n exec(`explorer \"${dir.replace(/\"/g, '')}\"`, (error) => {\n if (error) {\n writeJson(res, 500, { ok: false, error: error.message })\n } else {\n writeJson(res, 200, { ok: true })\n }\n })\n },\n }\n\n return [\n statusRoute,\n loadOutlineRoute,\n saveOutlineRoute,\n bibleRoute,\n volumesRoute,\n planRoute,\n generateRoute,\n reviewRoute,\n rewriteRoute,\n polishRoute,\n draftApplyRoute,\n draftDiscardRoute,\n summaryRoute,\n foreshadowRoute,\n exportRoute,\n chapterRoute,\n chapterCheckRoute,\n chapterSaveRoute,\n assetsRoute,\n styleEngineRoute,\n assistantRoute,\n assistantHistoryRoute,\n assistantClearRoute,\n bookshelfRoute,\n bookshelfActivateRoute,\n bookshelfRemoveRoute,\n resetRoute,\n auditRoute,\n charactersRefreshRoute,\n factsBackfillRoute,\n biblePatchRoute,\n blurbRoute,\n coverRoute,\n worldRoute,\n renameRoute,\n configRoute,\n openFolderRoute,\n ]\n}\n\n// Re-export for tests / type consumers.\nexport type {\n ConfigPatch,\n NovelConfig,\n StatusResponse,\n PlanResponse,\n LoadOutlineResponse,\n BibleResponse,\n VolumesResponse,\n ExportResponse,\n}\nexport { chapterFileName }\n","/**\n * dsh-novel-forge — host half. Mounts the AI novel-forge workbench: docx\n * outline import, LLM chapter planning, chapter-by-chapter generation\n * (3000-4000 chars each), Markdown output into your chosen folder, and the\n * /api/dsh-novel-forge route family. The browser half (./client) renders the\n * workbench panel. Everything rides official NPM SDK packages — no dsh source\n * changes.\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'\nimport z from 'schemastery'\nimport type {} from '@deepseek-ai/dsh-host-webserver'\nimport type {} from '@deepseek-ai/dsh-llm'\nimport type {} from '@deepseek-ai/dsh-system-prompt'\nimport type { ConfigPatch, NovelConfig } from './protocol.ts'\nimport { makeRoutes } from './routes.ts'\nimport { activeBookOutputDir } from './bookshelf.ts'\n\n/** Stable cordis plugin name. */\nexport const name = 'novel-forge'\n\n/** Services required before the novel-forge surfaces can mount. */\nexport const inject = ['webServer', 'llm', 'systemPrompt']\n\n/**\n * Settings namespace of the novel-forge capability — the section the web\n * settings surface edits. Spelled here rather than imported: the browser half\n * spells the same value and must not depend on a Host package.\n */\nexport const NOVEL_SETTINGS_NAMESPACE = settingsNamespace('dsh-novel-forge')\n\n/** Plugin config, validated by the same-named schemastery schema. */\nexport interface Config {\n /** When true (default), a system-prompt section announces the plugin to every agent. */\n announceToAgent?: boolean\n /** Master switch for the plugin (routes, prompt section). */\n enabled?: boolean\n /** Absolute path of the default docx outline to load. */\n outlinePath?: string\n /** Absolute output directory for chapters + project state. */\n outputDir?: string\n /** LLM provider route. */\n provider?: string\n /** LLM model id. */\n model?: string\n /** Target characters per chapter. */\n chapterChars?: number\n /** Max output tokens per chapter call. */\n maxTokens?: number\n /** Review pass threshold (0-100). */\n reviewPassScore?: number\n /** Whether generation auto-runs review after writing. */\n autoReview?: boolean\n}\n\nexport const Config: z<Config> = z.object({\n announceToAgent: z.boolean().default(true),\n enabled: z.boolean().default(true),\n outlinePath: z.string().default('C:\\\\Users\\\\Ryan\\\\Desktop\\\\《归墟玉主》全书大纲_重新排版版.docx'),\n outputDir: z.string().default('C:\\\\Users\\\\Ryan\\\\Desktop\\\\归墟玉主'),\n provider: z.string().default('deepseek-official'),\n model: z.string().default('deepseek-v4-flash'),\n chapterChars: z.number().default(3500),\n maxTokens: z.number().default(12000),\n reviewPassScore: z.number().default(70),\n autoReview: z.boolean().default(true),\n})\n\n/** Schema defaults, re-read for hand-built test contexts. */\nconst DEFAULT_ANNOUNCE = true\nconst DEFAULT_OUTLINE_PATH = 'C:\\\\Users\\\\Ryan\\\\Desktop\\\\《归墟玉主》全书大纲_重新排版版.docx'\nconst DEFAULT_OUTPUT_DIR = 'C:\\\\Users\\\\Ryan\\\\Desktop\\\\归墟玉主'\nconst DEFAULT_PROVIDER = 'deepseek-official'\nconst DEFAULT_MODEL = 'deepseek-v4-flash'\nconst DEFAULT_CHAPTER_CHARS = 3500\nconst DEFAULT_MAX_TOKENS = 12000\nconst DEFAULT_REVIEW_PASS_SCORE = 70\nconst DEFAULT_AUTO_REVIEW = true\n\n/** Order of the announcement section within the tool-guidance band. */\nconst SECTION_ORDER = 160\n\n/** Model-facing announcement: plugin presence, capabilities, and limits. */\nexport const NOVEL_GUIDANCE = '本机已安装 dsh-novel-forge 插件(AI 编译小说工作台):侧边栏「小说工坊」入口。能力:读取 docx 大纲(默认桌面《归墟玉主》大纲)或粘贴大纲文本;用 LLM 提炼道藏(人设/世界观/金手指规则/写作红线,即设定圣经);生成卷计划与章节计划;逐章调用 LLM 生成 3000-4000 字正文并保存为 Markdown(默认输出到 桌面\\\\归墟玉主);每章自动生成摘要(叙事记忆)、自动 AI 审稿(人设/设定/红线/文笔/爽点/逻辑),支持按审稿意见重写、去 AI 味润色、暗线(伏笔)管理、批量连写与全本导出(txt/md)。限制:生成消耗 LLM API 额度;输出目录与模型可在插件设置中修改;章节正文质量取决于大纲完整度。用户提到「小说 / 大纲 / 写小说 / 章节 / 审稿 / 润色 / 归墟玉主」时即指本插件,请据此协作。'\n\n/** Resolve a config-like value into the full runtime config. */\nexport function resolveConfig(value: Partial<Config> | undefined): NovelConfig {\n return {\n outlinePath: value?.outlinePath ?? DEFAULT_OUTLINE_PATH,\n outputDir: value?.outputDir ?? DEFAULT_OUTPUT_DIR,\n provider: value?.provider ?? DEFAULT_PROVIDER,\n model: value?.model ?? DEFAULT_MODEL,\n chapterChars: value?.chapterChars ?? DEFAULT_CHAPTER_CHARS,\n maxTokens: value?.maxTokens ?? DEFAULT_MAX_TOKENS,\n reviewPassScore: value?.reviewPassScore ?? DEFAULT_REVIEW_PASS_SCORE,\n autoReview: value?.autoReview ?? DEFAULT_AUTO_REVIEW,\n }\n}\n\n/**\n * Mount the routes and announcement.\n * @param ctx - host plugin context carrying webServer/llm/systemPrompt.\n * @param config - resolved plugin config (schema defaults applied by the loader).\n */\nexport function apply(ctx: Context, config?: Config): void {\n // The live source the routes read: the settings section once the web\n // settings surface is served, the composition entry otherwise.\n let current: () => Config = () => config ?? {}\n const resolve = (): NovelConfig => {\n const resolved = resolveConfig(current())\n // 书架激活的书优先决定输出目录(settings 仍可改默认值)。\n const shelfDir = activeBookOutputDir()\n if (shelfDir !== undefined) {\n return { ...resolved, outputDir: shelfDir }\n }\n return resolved\n }\n\n const patchConfig = async (patch: ConfigPatch): Promise<NovelConfig> => {\n const next: ConfigPatch = {}\n if (patch.outlinePath !== undefined) next.outlinePath = patch.outlinePath\n if (patch.outputDir !== undefined) next.outputDir = patch.outputDir\n if (patch.provider !== undefined) next.provider = patch.provider\n if (patch.model !== undefined) next.model = patch.model\n if (patch.chapterChars !== undefined) next.chapterChars = patch.chapterChars\n if (patch.maxTokens !== undefined) next.maxTokens = patch.maxTokens\n if (patch.reviewPassScore !== undefined) next.reviewPassScore = patch.reviewPassScore\n if (patch.autoReview !== undefined) next.autoReview = patch.autoReview\n // Persist through the settings seam when available; otherwise keep in memory.\n // (ctx.get is the non-strict service access — no inject requirement, same\n // pattern installSettingsSection itself uses.)\n const settings = ctx.get('settings')\n if (settings !== undefined) {\n await settings.update(NOVEL_SETTINGS_NAMESPACE, next as Record<string, unknown>)\n } else {\n current = () => ({ ...current(), ...next })\n }\n return resolve()\n }\n\n let disposeSection: (() => void) | undefined\n let disposeRoutes: (() => void) | undefined\n\n const sync = (): void => {\n if (disposeSection !== undefined) {\n disposeSection()\n disposeSection = undefined\n }\n if (disposeRoutes !== undefined) {\n disposeRoutes()\n disposeRoutes = undefined\n }\n const value = resolve()\n if (!(current().enabled ?? true)) return\n if (current().announceToAgent ?? DEFAULT_ANNOUNCE) {\n disposeSection = ctx.systemPrompt.section({\n name: 'plugin:dsh-novel-forge',\n order: SECTION_ORDER,\n text: NOVEL_GUIDANCE,\n })\n }\n const routes = makeRoutes({ ctx, getConfig: resolve, patchConfig })\n disposeRoutes = ctx.effect(\n () => {\n const disposers = routes.map(route => ctx.webServer.register(route))\n return () => { for (const dispose of disposers) dispose() }\n },\n 'dsh-novel-forge: routes',\n )\n void value\n }\n\n installSettingsSection(ctx, NOVEL_SETTINGS_NAMESPACE, Config, config ?? {}, {\n setSource: (source) => {\n current = source\n sync()\n },\n onChange: sync,\n })\n\n // Initial registration from the composition entry.\n sync()\n}\n"],"mappings":"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ACF;EACA,eAAe,CACb,qBACA,aACF;EACA,eAAe,CACb,iBACA,iBACF;EACA,aAAa,CACX,qBACA,cACF;EACA,uBAAuB;GAAC;GAAU;GAAU;GAAW;EAAU;CACnE;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,UAAU;EACV,kBAAkB;GAAC;GAAM;GAAM;EAAI;EACnC,YAAY,CACV,gCACA,gBACF;EACA,eAAe,CACb,yBACF;EACA,eAAe,CACb,kBACA,WACF;EACA,aAAa,CACX,mBACA,SACF;EACA,uBAAuB;GAAC;GAAW;GAAW;EAAQ;CACxD;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,UAAU;EACV,kBAAkB;GAAC;GAAM;GAAM;EAAI;EACnC,YAAY,CACV,4BACA,iBACF;EACA,eAAe,CACb,oBACA,WACF;EACA,eAAe,CACb,oBACA,eACF;EACA,aAAa,CACX,gBACA,UACF;EACA,uBAAuB;GAAC;GAAU;GAAY;EAAU;CAC1D;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,UAAU;EACV,kBAAkB;GAAC;GAAM;GAAM;EAAI;EACnC,YAAY;GACV;GACA;GACA;EACF;EACA,eAAe,CACb,mBACA,gBACF;EACA,eAAe,CACb,mBACA,WACF;EACA,aAAa,CACX,iBACA,gBACF;EACA,uBAAuB;GAAC;GAAU;GAAU;EAAS;CACvD;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,UAAU;EACV,kBAAkB;GAAC;GAAM;GAAQ;EAAI;EACrC,YAAY;GACV;GACA;GACA;EACF;EACA,eAAe,CACb,gBACA,mBACF;EACA,eAAe,CACb,mBACA,aACF;EACA,aAAa,CACX,mBACA,gBACF;EACA,uBAAuB;GAAC;GAAa;GAAU;GAAU;EAAQ;CACnE;AACF;;AAKA,MAAa,wBAAsC;CACjD;EACE,MAAM;EACN,OAAO;EACP,KAAK;EACL,gBAAgB;GAAC;GAAO;GAAO;GAAQ;GAAQ;GAAQ;EAAM;EAC7D,SAAS;CACX;CACA;EACE,MAAM;EACN,OAAO;EACP,KAAK;EACL,gBAAgB;GAAC;GAAQ;G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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/protocol.ts","../src/docx.ts","../src/assets.ts","../src/engine.ts","../src/assistant.ts","../src/bookshelf.ts","../src/routes.ts","../src/index.ts"],"sourcesContent":["/**\n * dsh-novel-forge — shared protocol between the host half (Node) and the\n * browser half (web GUI). Route paths, request/response shapes, the project\n * state file format, and the NDJSON generation stream frames all live here so\n * both halves spell exactly one vocabulary.\n */\n\n/** The /api/dsh-novel-forge route family (same-origin, loopback-fenced). */\nexport const NOVEL_API = {\n status: '/api/dsh-novel-forge/status',\n loadOutline: '/api/dsh-novel-forge/load-outline',\n saveOutline: '/api/dsh-novel-forge/save-outline',\n plan: '/api/dsh-novel-forge/plan',\n volumes: '/api/dsh-novel-forge/volumes',\n bible: '/api/dsh-novel-forge/bible',\n assets: '/api/dsh-novel-forge/assets',\n styleEngine: '/api/dsh-novel-forge/style-engine',\n generate: '/api/dsh-novel-forge/generate',\n review: '/api/dsh-novel-forge/review',\n rewrite: '/api/dsh-novel-forge/rewrite',\n polish: '/api/dsh-novel-forge/polish',\n /** 采纳待确认草稿(润色/重写产物)覆盖正文文件。 */\n draftApply: '/api/dsh-novel-forge/draft/apply',\n /** 放弃待确认草稿,保留原稿。 */\n draftDiscard: '/api/dsh-novel-forge/draft/discard',\n summary: '/api/dsh-novel-forge/summary',\n foreshadow: '/api/dsh-novel-forge/foreshadow',\n exportBook: '/api/dsh-novel-forge/export',\n chapter: '/api/dsh-novel-forge/chapter',\n /** 审查任意正文文本(作者手动编辑后,不落盘)。 */\n chapterCheck: '/api/dsh-novel-forge/chapter/check',\n /** 保存手动编辑的正文(自动备份 .bak)。 */\n chapterSave: '/api/dsh-novel-forge/chapter/save',\n assistant: '/api/dsh-novel-forge/assistant',\n assistantHistory: '/api/dsh-novel-forge/assistant-history',\n /** 清空助手对话记录。 */\n assistantClear: '/api/dsh-novel-forge/assistant/clear',\n bookshelf: '/api/dsh-novel-forge/bookshelf',\n /** 重置项目(可选携带新大纲):清空设定/卷/章节/伏笔/资产/事实库。 */\n reset: '/api/dsh-novel-forge/reset',\n /** 全书一致性质检:LLM 扫描已生成章节,输出矛盾问题清单。 */\n audit: '/api/dsh-novel-forge/audit',\n /** 角色卡刷新:基于事实库与各章摘要聚合角色当前状态。 */\n charactersRefresh: '/api/dsh-novel-forge/characters/refresh',\n /** 事实库回填:对历史已生成章节批量抽取事实(旧章节无事实记录时用)。 */\n factsBackfill: '/api/dsh-novel-forge/facts/backfill',\n /** 设定圣经局部修补(如世界观规则编辑)。 */\n biblePatch: '/api/dsh-novel-forge/bible/patch',\n /** 小说简介:生成(AI)/补全(AI)/保存。 */\n blurb: '/api/dsh-novel-forge/blurb',\n /** 重命名当前书(同步项目与书架条目)。 */\n rename: '/api/dsh-novel-forge/rename',\n /** 大世界:AI 提炼 / 保存结构化数据(境界/区域/势力)。 */\n world: '/api/dsh-novel-forge/world',\n /** 封面:GET 读取(dataUrl)/ POST 上传或移除。 */\n cover: '/api/dsh-novel-forge/blurb/cover',\n config: '/api/dsh-novel-forge/config',\n openFolder: '/api/dsh-novel-forge/open-folder',\n} as const\n\n/** 书架:一本书的条目。 */\nexport interface BookEntry {\n /** 稳定 id。 */\n id: string\n /** 书名。 */\n bookName: string\n /** 该书输出目录(独立项目目录)。 */\n outputDir: string\n /** 创建时间。 */\n createdAt: string\n /** 最后活动时间。 */\n updatedAt: string\n}\n\n/** 书架快照(含每本书的进度摘要)。 */\nexport interface BookshelfSnapshot {\n books: Array<BookEntry & { done: number; total: number; hasProject: boolean; hasCover: boolean; blurb?: string }>\n /** 当前激活的书 id(无则 null)。 */\n activeBookId: string | null\n}\n\n/** POST /bookshelf 请求:创建新书。 */\nexport interface BookCreateRequest {\n bookName: string\n outputDir?: string\n /** 开书向导:创建时直接导入的大纲文本(提供则立即建立项目)。 */\n outline?: string\n}\n\n/** POST /reset 请求:重置项目(可选更新大纲)。 */\nexport interface ResetRequest {\n /** 新大纲文本;提供则替换 outline,否则保留原大纲。 */\n outline?: string\n}\n\n/** POST /bookshelf/activate 请求:切换当前书。 */\nexport interface BookActivateRequest {\n id: string\n}\n\n/** POST /bookshelf/remove 请求:移除书架条目。 */\nexport interface BookRemoveRequest {\n id: string\n}\n\n/** Chapter lifecycle states (the writing pipeline's state machine). */\nexport type ChapterStatus =\n | 'pending' // planned, not started\n | 'generating' // LLM writing right now\n | 'written' // body on disk, awaiting review\n | 'reviewing' // review in progress\n | 'approved' // passed review (or user-approved)\n | 'rejected' // review found problems\n | 'error' // generation failed\n\n/** One chapter in the plan. */\nexport interface ChapterPlan {\n /** 1-based chapter number (stable identity; files are named from it). */\n no: number\n /** Volume this chapter belongs to (1-based; 0 = unassigned). */\n volume: number\n /** Chapter title, decided by the LLM plan step. */\n title: string\n /** Story beats / plot points for this chapter (model-facing guidance). */\n beats: string\n /** Target character count (defaults to the configured chapter size). */\n targetChars: number\n /** Generation/review state. */\n status: ChapterStatus\n /** Actual character count once generated. */\n chars?: number\n /** Failure message when status is 'error'. */\n error?: string\n /** Output file name once generated (relative to the output dir). */\n file?: string\n /** LLM summary of the chapter (narrative memory for later chapters). */\n summary?: string\n /** Latest review report (present once reviewed). */\n review?: ReviewReport\n /**\n * 待确认草稿:润色(去AI味)或整章重写的产物正文。生成时先存这里,\n * 用户看过对比后点「采纳」才覆盖正文文件;点「放弃」则丢弃。刷新页面不丢失。\n */\n pendingDraft?: string\n}\n\n/** One review finding. */\nexport interface ReviewIssue {\n /** Severity: high = must fix, medium = should fix, low = suggestion. */\n severity: 'high' | 'medium' | 'low'\n /** What the problem is. */\n item: string\n /** Concrete suggestion for fixing it. */\n suggestion: string\n}\n\n/** AI review report for one chapter. */\nexport interface ReviewReport {\n /** Overall score 0-100. */\n score: number\n /** Pass threshold (config; 70 default). */\n passed: boolean\n /** One-line verdict. */\n verdict: string\n /** Individual findings. */\n issues: ReviewIssue[]\n /** When the review ran. */\n reviewedAt: string\n}\n\n/** A volume of the book. */\nexport interface Volume {\n /** 1-based volume number. */\n no: number\n /** Volume title. */\n title: string\n /** Volume positioning / summary. */\n summary: string\n /** First chapter number of this volume. */\n chapterStart: number\n /** Last chapter number (inclusive). */\n chapterEnd: number\n}\n\n/** A character card from the story bible. */\nexport interface CharacterCard {\n name: string\n role: 'protagonist' | 'supporting' | 'antagonist' | 'other'\n /** Personality / traits (short lines). */\n traits: string[]\n /** Goals and motivations. */\n goals: string\n /** Key relations to other characters. */\n relations: string\n}\n\n/** The structured story bible (worldbuilding extracted from the outline). */\nexport interface StoryBible {\n /** Genre + tone tags. */\n genre: string\n /** Worldbuilding rules (power system, geography, factions...). */\n worldRules: string[]\n /** Character cards. */\n characters: CharacterCard[]\n /** Writing red lines (forbidden content / must-avoid tropes). */\n redLines: string[]\n /** Style guidance (pacing, pov, tone). */\n style: string[]\n /** When the bible was generated. */\n generatedAt?: string\n}\n\n/** A planted/active/resolved foreshadowing thread. */\nexport interface Foreshadow {\n /** Stable id. */\n id: string\n /** What the foreshadow is. */\n description: string\n /** Chapter where it was planted (undefined = planned). */\n plantedChapter?: number\n /** Chapter where it should be paid off. */\n targetChapter?: number\n /** Lifecycle state. */\n status: 'planned' | 'planted' | 'progressing' | 'resolved' | 'abandoned'\n /** Resolution note when resolved. */\n resolvedNote?: string\n}\n\n/** 一条已确立的叙事事实(事实库/时间线,注入后续章节生成)。 */\nexport interface ChapterFact {\n /** 来源章节号。 */\n chapterNo: number\n /** 事实文本(人物状态/境界资源/关系变化/伏笔落地等)。 */\n text: string\n}\n\n/** 一条全书质检发现的问题(一致性矛盾,定位到章)。 */\nexport interface AuditIssue {\n /** 问题所在章节号(无法定位时 0)。 */\n chapterNo: number\n severity: 'high' | 'medium' | 'low'\n /** 矛盾描述。 */\n item: string\n /** 修改建议。 */\n suggestion: string\n}\n\n/** POST /audit 响应。 */\nexport interface AuditResponse {\n issues: AuditIssue[]\n /** 参与质检的章节数。 */\n auditedChapters: number\n /** 质检时间。 */\n auditedAt: string\n}\n\n/** 角色卡:角色当前状态(从事实库聚合)。 */\nexport interface RoleStatusCard {\n name: string\n /** protagonist / supporting / antagonist / other。 */\n role: string\n /** 当前状态一句话(境界/资源/伤势/心境)。 */\n status: string\n /** 最近出场章节。 */\n lastChapter: number\n /** 出场次数。 */\n appearances: number\n}\n\n/** POST /bible/patch 请求:局部修补设定圣经。 */\nexport interface BiblePatchRequest {\n worldRules?: string[]\n redLines?: string[]\n style?: string[]\n}\n\n/** POST /blurb 请求:AI 生成/补全或手动保存小说简介。 */\nexport interface BlurbRequest {\n action: 'generate' | 'save'\n /** 已写好的开头(AI 补全时使用;留空 = 全量生成)。 */\n partial?: string\n /** 手动保存的完整简介(action=save 时)。 */\n text?: string\n}\n\n/** POST /chapter/check|save 请求:审查/保存手动编辑的正文。 */\nexport interface ChapterTextRequest {\n chapterNo: number\n /** 当前编辑中的正文全文。 */\n text: string\n /** 保存时携带:已在工作区审查过的报告(沿用落盘,不重复审);缺省则保存后自动正式审稿一次。 */\n report?: ReviewReport\n}\n\n/** POST /chapter/save 响应。 */\nexport interface ChapterSaveResponse {\n ok: boolean\n chars: number\n file: string\n /** 落盘的审稿报告(沿用工作区报告或保存后自动审稿)。 */\n report?: ReviewReport\n}\n\n/** POST /cover 请求:上传或移除封面。 */\nexport interface CoverRequest {\n action: 'upload' | 'remove'\n /** 上传时:data:image/...;base64,... 格式的图片数据。 */\n dataUrl?: string\n}\n\n/** POST /rename 请求:重命名当前书。 */\nexport interface RenameRequest {\n bookName: string\n}\n\n/** 大世界:一个境界等级。 */\nexport interface WorldRealm {\n /** 境界名(练气/筑基/金丹…)。 */\n name: string\n /** 描述:突破条件/寿命/标志等。 */\n description: string\n}\n\n/** 大世界:一个地理区域。 */\nexport interface WorldRegion {\n name: string\n description: string\n /** 关联势力名(可空)。 */\n faction?: string\n}\n\n/** 大世界:一方势力。 */\nexport interface WorldFaction {\n name: string\n /** 类型:宗门/家族/王朝/组织… */\n kind: string\n description: string\n /** 驻地区域(可空)。 */\n region?: string\n}\n\n/** 大世界结构化数据。 */\nexport interface WorldState {\n realms: WorldRealm[]\n regions: WorldRegion[]\n factions: WorldFaction[]\n}\n\n/** POST /world 请求:AI 提炼或手动保存。 */\nexport interface WorldRequest {\n action: 'generate' | 'save'\n /** action=save 时的完整世界数据。 */\n world?: WorldState\n}\n\n/** GET /cover 响应:封面的 dataUrl(无封面为 null)。 */\nexport interface CoverResponse {\n dataUrl: string | null\n}\n\n/** The persisted project: outline + bible + plan + progress. */\nexport interface ProjectState {\n /** Book title (first non-empty line of the outline, usually). */\n bookName: string\n /** Full outline text (docx-extracted or pasted). */\n outline: string\n /** Source outline path when loaded from a docx. */\n outlinePath?: string\n /** Structured story bible (worldbuilding), if generated. */\n bible?: StoryBible\n /** Volumes, if planned. */\n volumes?: Volume[]\n /** Chapter plan. */\n chapters: ChapterPlan[]\n /** Foreshadowing threads. */\n foreshadows: Foreshadow[]\n /** 写作资产(题材基底/推进模式/反AI规则/写法资产)。 */\n assets?: ProjectAssets\n /** 事实库/时间线:每章生成后抽取,注入后续章节保持一致性。 */\n facts?: ChapterFact[]\n /** 小说简介(面向读者的作品门面,AI 生成或手动保存)。 */\n blurb?: string\n /** 封面文件名(相对输出目录,如 cover.png)。 */\n coverPath?: string\n /** 大世界结构化数据(境界体系/区域/势力)。 */\n world?: WorldState\n /** ISO timestamps. */\n createdAt: string\n updatedAt: string\n}\n\n/** Runtime config surface exposed to the panel (subset of plugin Config). */\nexport interface NovelConfig {\n /** Absolute path of the default docx outline to load. */\n outlinePath: string\n /** Absolute output directory for chapters + project state. */\n outputDir: string\n /** LLM provider route (e.g. deepseek-official). */\n provider: string\n /** LLM model id (e.g. deepseek-v4-flash). */\n model: string\n /** Target characters per chapter. */\n chapterChars: number\n /** Max output tokens per chapter call. */\n maxTokens: number\n /** Review pass threshold (0-100). */\n reviewPassScore: number\n /** Whether generation auto-runs review after writing. */\n autoReview: boolean\n}\n\n/** GET /status response. */\nexport interface StatusResponse {\n config: NovelConfig\n /** The persisted project, when one exists in the output dir. */\n project?: ProjectState\n /** Chapter files already on disk (basenames, sorted). */\n generatedFiles: string[]\n}\n\n/** POST /load-outline request: either a docx path or raw text. */\nexport interface LoadOutlineRequest {\n /** Absolute docx path; defaults to the configured outline path. */\n path?: string\n /** Raw outline text (takes precedence over path when present). */\n text?: string\n}\n\n/** POST /load-outline response. */\nexport interface LoadOutlineResponse {\n outline: string\n bookName: string\n chars: number\n path?: string\n}\n\n/** POST /plan request. */\nexport interface PlanRequest {\n /** Outline to plan from; defaults to the persisted project's outline. */\n outline?: string\n /** Number of chapters to plan (default: 30). */\n chapterCount?: number\n /** Volume to plan (1-based); when given, plans only that volume's chapters. */\n volume?: number\n}\n\n/** POST /plan response. */\nexport interface PlanResponse {\n chapters: ChapterPlan[]\n volumes?: Volume[]\n}\n\n/** POST /volumes request/response. */\nexport interface VolumesRequest {\n /** Outline to split into volumes; defaults to the project outline. */\n outline?: string\n}\nexport interface VolumesResponse {\n volumes: Volume[]\n}\n\n/** POST /bible request/response. */\nexport interface BibleRequest {\n /** Outline to extract from; defaults to the project outline. */\n outline?: string\n}\nexport interface BibleResponse {\n bible: StoryBible\n}\n\n/** POST /generate request: one chapter of the current project. */\nexport interface GenerateRequest {\n chapterNo: number\n /** When true, skips the auto-review step. */\n skipReview?: boolean\n}\n\n/** One NDJSON frame of a generation/review/rewrite stream. */\nexport type JobFrame =\n | { type: 'start'; no: number; title: string }\n | { type: 'delta'; text: string }\n | { type: 'progress'; chars: number }\n | { type: 'done'; no: number; file: string; chars: number; title: string }\n | { type: 'review'; no: number; report: ReviewReport }\n | { type: 'rewritten'; no: number; file: string; chars: number }\n /** 润色/重写完成,产物作为待确认草稿(尚未覆盖正文)。 */\n | { type: 'drafted'; no: number; chars: number; draft: string }\n | { type: 'error'; no: number; message: string }\n\n/** POST /review request: review one written chapter. */\nexport interface ReviewRequest {\n chapterNo: number\n}\n\n/** POST /rewrite request: rewrite one chapter (optionally per review issues). */\nexport interface RewriteRequest {\n chapterNo: number\n /** Free-form instructions; defaults to fixing the review's high issues. */\n instructions?: string\n /**\n * 局部修订:正文中的一段原文(无需完全精确,取一个自然段内的片段即可)。\n * 提供时只重写该段,其余正文保持不变;不提供时整章重写。\n */\n target?: string\n}\n\n/** POST /polish request: de-AI-ify one chapter. */\nexport interface PolishRequest {\n chapterNo: number\n}\n\n/** POST /draft/apply | /draft/discard request: 采纳或放弃待确认草稿。 */\nexport interface DraftDecisionRequest {\n chapterNo: number\n}\n\n/** POST /summary request: (re)generate a chapter summary. */\nexport interface SummaryRequest {\n chapterNo: number\n}\n\n/** POST /foreshadow request: create, update, or AI-suggest foreshadows. */\nexport interface ForeshadowRequest {\n /** When true, runs the LLM suggestion pass (ignores other fields). */\n suggest?: boolean\n /** When given, updates that foreshadow instead of creating one. */\n id?: string\n description?: string\n plantedChapter?: number\n targetChapter?: number\n status?: Foreshadow['status']\n resolvedNote?: string\n}\nexport interface ForeshadowResponse {\n foreshadows: Foreshadow[]\n}\n\n/** GET /chapter response. */\nexport interface ChapterResponse {\n no: number\n title: string\n markdown: string\n}\n\n/** POST /export request/response. */\nexport interface ExportRequest {\n format: 'txt' | 'md'\n}\nexport interface ExportResponse {\n file: string\n chars: number\n chapters: number\n}\n\n/** POST /config request: patch any subset of the runtime config. */\nexport interface ConfigPatch {\n outlinePath?: string\n outputDir?: string\n provider?: string\n model?: string\n chapterChars?: number\n maxTokens?: number\n reviewPassScore?: number\n autoReview?: boolean\n}\n\n// ------------------------------------------------------------ assistant\n\n/** One assistant conversation message (persisted per project). */\nexport interface AssistantMessage {\n role: 'user' | 'assistant' | 'tool'\n /** Message text (tool messages carry the tool result). */\n content: string\n /** ISO timestamp. */\n ts: string\n /** For tool messages: which tool ran. */\n tool?: string\n}\n\n/** POST /assistant request: one user turn. */\nexport interface AssistantRequest {\n message: string\n}\n\n/** One NDJSON frame of the assistant stream. */\nexport type AssistantFrame =\n | { type: 'delta'; text: string }\n | { type: 'tool'; name: string; status: 'start' | 'done' | 'error'; detail?: string }\n /** Live output while a tool runs (e.g. chapter text being generated). */\n | { type: 'toolDelta'; name: string; text: string }\n | { type: 'done' }\n | { type: 'error'; message: string }\n\n/** GET /assistant-history response. */\nexport interface AssistantHistoryResponse {\n messages: AssistantMessage[]\n}\n\n// ---------------------------------------------------------- writing assets\n\n/** 题材基底库:一本书属于哪个阅读市场。树形(题材→子题材→下级)。 */\nexport interface GenreNode {\n /** 题材名称(标签,如「仙侠修真」「都市异能」)。 */\n name: string\n /** 题材特征、常见爽点、叙事重心或读者期待。 */\n description: string\n /** 子题材。 */\n children: GenreNode[]\n}\n\n/** 推进模式库:读者为什么继续看下一章。 */\nexport interface ProgressionMode {\n /** 模式名称(如「升级变强」「经营扩张」「解谜揭露」)。 */\n name: string\n /** 核心驱动力:靠什么制造追读动力。 */\n driver: string\n /** 读者期待:每隔几章获得什么变化或回报。 */\n readerExpectation: string\n /** 常见兑现方式(爽点如何落地)。 */\n payoffs: string[]\n /** 节奏风险:最怕什么(重复升级、冲突变弱、谜题拖太久…)。 */\n risks: string[]\n /** 主模式或辅助模式。 */\n primary: boolean\n}\n\n/** 反 AI 规则:一条「要避免的问题 + 修正方向」。 */\nexport interface AntiAiRule {\n /** 规则名(如「禁止解释型心理描写」「AI 高频套话」)。 */\n name: string\n /** 要避免的表达问题,具体可检查。 */\n avoid: string\n /** 推荐修正方向。 */\n fix: string\n /** 命中即告警的具体表达模式(用于审稿逐条核对与去 AI 味检测)。 */\n detectPatterns?: string[]\n /** 是否内置全局规则(内置规则随插件发布,项目规则为用户自定义)。 */\n builtin?: boolean\n}\n\n/** 预置写法模板(来自 AI-Novel-Writing-Assistant 内置数据,一键绑定无需样本文本)。 */\nexport interface StyleTemplate {\n /** 模板 key(如 power-up-escalation)。 */\n key: string\n /** 模板名(如「爽文递进推进流」)。 */\n name: string\n /** 模板说明。 */\n description: string\n /** 分类(如「爽文流」「悬疑流」)。 */\n category: string\n /** 适用题材。 */\n applicableGenres: string[]\n /** 叙述规则。 */\n proseRules: string[]\n /** 角色/台词规则。 */\n dialogueRules: string[]\n /** 语言规则。 */\n languageRules: string[]\n /** 节奏规则。 */\n rhythmRules: string[]\n /** 该模板默认绑定的反 AI 规则 key(内置规则名)。 */\n defaultAntiAiRuleKeys: string[]\n}\n\n/** 写法引擎:从样本文本提取的叙事风格资产。 */\nexport interface StyleAsset {\n /** 资产名(如「林越式痞坏」「冷峻猎手风」)。 */\n name: string\n /** 叙述视角与句式节奏。 */\n proseRules: string[]\n /** 角色台词风格。 */\n dialogueRules: string[]\n /** 描写密度与情绪表达。 */\n descriptionRules: string[]\n /** 表达边界(不要做什么)。 */\n boundaries: string[]\n /** 来源样本文本(可空)。 */\n sourceText?: string\n /** 创建时间。 */\n createdAt: string\n}\n\n/** 项目写作资产(题材基底 + 推进模式 + 反 AI 规则 + 写法资产)。 */\nexport interface ProjectAssets {\n /** 本书选用的题材(可以是题材基底库中某节点名)。 */\n genre?: GenreNode\n /** 主推进模式。 */\n primaryProgression?: ProgressionMode\n /** 辅助推进模式。 */\n auxiliaryProgressions: ProgressionMode[]\n /** 生效的反 AI 规则(内置 + 自定义)。 */\n antiAiRules: AntiAiRule[]\n /** 绑定的写法资产。 */\n styleAssets: StyleAsset[]\n /** 资产更新时间。 */\n updatedAt?: string\n}\n\n/** GET /assets response(含全局题材库与反 AI 规则库)。 */\nexport interface AssetsResponse {\n projectAssets: ProjectAssets\n /** 全局题材基底库(可复用资产,跨书)。 */\n genreLibrary: GenreNode[]\n /** 全局反 AI 规则库(内置默认规则)。 */\n antiAiLibrary: AntiAiRule[]\n /** 预置写法模板(一键绑定,无需样本文本)。 */\n styleTemplates: StyleTemplate[]\n /** 内置推进模式候选。 */\n progressionLibrary: ProgressionMode[]\n}\n\n/** POST /assets request:更新项目写作资产(部分字段可选)。 */\nexport interface AssetsPatch {\n genre?: GenreNode\n primaryProgression?: ProgressionMode\n auxiliaryProgressions?: ProgressionMode[]\n antiAiRules?: AntiAiRule[]\n styleAssets?: StyleAsset[]\n}\n\n/** POST /style-engine request:从样本文本提取写法资产。 */\nexport interface StyleEngineRequest {\n /** 样本文本(风格来源)。 */\n sampleText: string\n /** 资产名(可选,默认「风格资产 N」)。 */\n name?: string\n}\n","/**\n * docx outline extraction: a .docx is a zip whose word/document.xml holds the\n * body text in <w:t> runs inside <w:p> paragraphs. We unzip with fflate and\n * walk the XML with a tiny tokenizer — no heavyweight XML/DOM dependency.\n */\n\nimport { readFileSync } from 'node:fs'\nimport { unzipSync, strFromU8 } from 'fflate'\n\n/** Decode the handful of XML entities docx bodies actually use. */\nfunction decodeEntities(text: string): string {\n return text\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&apos;/g, \"'\")\n .replace(/&amp;/g, '&')\n .replace(/&nbsp;/g, ' ')\n}\n\n/**\n * Extract plain text from a docx buffer: one line per <w:p> paragraph, with\n * <w:tab>/<w:br> preserved as whitespace. Tables and nested structures are\n * flattened in document order (their paragraphs are just <w:p> too).\n * @param buffer - the raw .docx bytes.\n * @returns the body text.\n */\nexport function extractDocxText(buffer: Uint8Array): string {\n let files: ReturnType<typeof unzipSync>\n try {\n files = unzipSync(buffer)\n } catch (error) {\n throw new Error(`not a valid docx (zip open failed): ${(error as Error).message}`)\n }\n const document = files['word/document.xml']\n if (document === undefined) {\n throw new Error('not a valid docx (word/document.xml missing)')\n }\n const xml = strFromU8(document)\n\n const paragraphs: string[] = []\n // Split on paragraph boundaries; keep the segment text of each.\n const parts = xml.split(/<w:p\\b[^>]*>/)\n for (let i = 1; i < parts.length; i++) {\n const segment = parts[i]!\n // Runs <w:t ...>…</w:t>; also honor <w:tab/> and <w:br/> as spaces.\n const runs: string[] = []\n const runRe = /<w:t(?:\\s[^>]*)?>([\\s\\S]*?)<\\/w:t>|<w:tab\\b[^>]*\\/>|<w:br\\b[^>]*\\/>/g\n for (const match of segment.matchAll(runRe)) {\n if (match[0].startsWith('<w:tab')) {\n runs.push('\\t')\n } else if (match[0].startsWith('<w:br')) {\n runs.push('\\n')\n } else {\n runs.push(decodeEntities(match[1] ?? ''))\n }\n }\n const line = runs.join('').replace(/\\u00a0/g, ' ').trimEnd()\n paragraphs.push(line)\n }\n\n // Collapse 3+ blank lines and trim.\n const text = paragraphs.join('\\n').replace(/\\n{3,}/g, '\\n\\n').trim()\n if (text.length === 0) {\n throw new Error('docx contains no extractable text')\n }\n return text\n}\n\n/**\n * Read and extract a docx outline from disk.\n * @param path - absolute path to the .docx file.\n * @returns the extracted outline text.\n */\nexport function readOutlineFromDocx(path: string): string {\n let buffer: Buffer\n try {\n buffer = readFileSync(path)\n } catch (error) {\n throw new Error(`cannot read outline file \"${path}\": ${(error as Error).message}`)\n }\n return extractDocxText(new Uint8Array(buffer))\n}\n","/**\n * Writing assets — 题材基底库 / 推进模式库 / 反 AI 规则 / 写法引擎.\n *\n * Ported from AI-Novel-Writing-Assistant (Apache-2.0) built-in seed data:\n * - 8 preset style templates (DEFAULT_STYLE_TEMPLATES)\n * - 12 anti-AI rules with concrete detect patterns (DEFAULT_ANTI_AI_RULES)\n * - genre tree + progression mode seeds\n * Assets persist with the project (novel-project.json) and are injected into\n * generation / planning / review prompts, and available to the AI assistant.\n */\n\nimport type { AntiAiRule, GenreNode, ProgressionMode, ProjectAssets, StyleAsset, StyleTemplate } from './protocol.ts'\n\n// ------------------------------------------------------ built-in style templates\n\n/** 预置写法模板(来自 AI-Novel-Writing-Assistant 内置 DEFAULT_STYLE_TEMPLATES)。 */\nexport const BUILTIN_STYLE_TEMPLATES: StyleTemplate[] = [\n {\n key: 'power-up-escalation',\n name: '爽文递进推进流',\n description: '持续升级冲突和收益点,强化目标推进与爽点兑现。',\n category: '爽文流',\n applicableGenres: ['都市', '玄幻', '热血'],\n proseRules: [\n '围绕目标推进,尽快兑现局部收益;每段都要有目标推进或爽点兑现。',\n '保持明确因果和节奏抬升,场景单元按「目标→阻碍→压制→反转收益」推进。',\n '优先冲突和结果,少停留;段尾用钩子收束。',\n ],\n dialogueRules: [\n '角色表达直接,情绪跟随胜负切换。',\n '对话承担推进与信息功能,但保留角色自己的语气差异。',\n ],\n languageRules: [\n '句式清晰,减少无效分散信息。',\n '直接、明确,不做无谓铺垫。',\n ],\n rhythmRules: [\n '快节奏,段落密度中等,动作先于解释。',\n '尽快兑现局部收益,避免拖沓。',\n ],\n defaultAntiAiRuleKeys: ['禁止总结主题', '对话纯功能推进', '连续三段解释性叙事'],\n },\n {\n key: 'bottom-loop-reality',\n name: '底层循环现实流',\n description: '通过碎片化生活与反复落空表现人物困境。',\n category: '现实流',\n applicableGenres: ['都市', '现实', '成长'],\n proseRules: [\n '以时间推进和现实落差构成叙事张力,结尾不解决核心困境。',\n '场景单元按「行为→落差→自我合理化」推进。',\n '以碎片化生活推进,不做总括式回顾。',\n ],\n dialogueRules: [\n '人物情绪通过动作和嘴硬表达,允许短促口语化台词。',\n '对话保留生活杂音与无效信息。',\n ],\n languageRules: [\n '语言粗粝、口语化,允许生活杂音与不完整句。',\n '句子变化度高,允许无意义细节。',\n ],\n rhythmRules: [\n '段落密实,动作先于解释。',\n '中快节奏,允许碎片化流动。',\n ],\n defaultAntiAiRuleKeys: ['禁止解释型心理描写', '禁止段尾升华', '鼓励无意义小动作', '鼓励现实落差', '鼓励嘴硬补偿'],\n },\n {\n key: 'suspense-pressure',\n name: '悬疑压迫递增流',\n description: '通过信息遮蔽、细节异常和压力叠加制造不安感。',\n category: '悬疑流',\n applicableGenres: ['悬疑', '惊悚', '现实'],\n proseRules: [\n '以异常细节、信息差和节奏收束推动悬念层层加压。',\n '场景单元按「现场细节→异常→误判→新风险」推进。',\n '优先制造信息缺口和压迫氛围。',\n ],\n dialogueRules: [\n '角色反应克制,恐惧通过反应显现。',\n '对话保留克制感,不解释恐惧来源。',\n ],\n languageRules: [\n '细节精确,保留少量噪音增强现场感。',\n '克制、中等偏高句变化。',\n ],\n rhythmRules: [\n '通过节奏收束和信息延迟制造压力。',\n '中速,段落密度中等偏高。',\n ],\n defaultAntiAiRuleKeys: ['禁止解释型心理描写', '禁止总结主题', '段落长度过于整齐', '鼓励现实落差'],\n },\n {\n key: 'emotional-tension',\n name: '情绪拉扯流',\n description: '通过错位表达、停顿和误读制造关系张力。',\n category: '情感流',\n applicableGenres: ['言情', '都市', '群像'],\n proseRules: [\n '人物不直说核心情绪,靠误读、停顿和反应推动关系变化。',\n '场景单元按「动作→言外之意→误读→回避」推进。',\n '以关系错位推进,而非直接说明。',\n ],\n dialogueRules: [\n '情绪通过停顿、动作和言外之意体现。',\n '对话充满潜台词与试探。',\n ],\n languageRules: [\n '语言自然,允许留白与停顿。',\n '句子变化度高,允许无意义细节。',\n ],\n rhythmRules: [\n '给关系反应留空间,但避免空洞抒情。',\n '中慢节奏,段落密度中等。',\n ],\n defaultAntiAiRuleKeys: ['禁止直接说教', '禁止段尾升华', '对话纯功能推进', '鼓励无意义小动作'],\n },\n {\n key: 'ensemble-weave',\n name: '群像交织流',\n description: '以多人行动线和视角差异交织推进事件。',\n category: '群像流',\n applicableGenres: ['群像', '都市', '悬疑'],\n proseRules: [\n '多角色并行推进,但每个角色的表达和认知范围必须区分清楚。',\n '多线并进,但视角切换要受控。',\n ],\n dialogueRules: [\n '不同角色口吻必须拉开差异,避免所有人说话一样。',\n ],\n languageRules: [\n '保持角色差异,句式变化度高。',\n '减少无效分散信息。',\n ],\n rhythmRules: [\n '多线交织但节奏不乱,平衡推进。',\n '动作先于解释。',\n ],\n defaultAntiAiRuleKeys: ['对话纯功能推进', '句式重复率过高', '禁止总结主题'],\n },\n {\n key: 'immersive-daily',\n name: '日常浸没流',\n description: '通过生活细节和细微情绪变化建立持续沉浸感。',\n category: '日常流',\n applicableGenres: ['日常', '治愈', '都市'],\n proseRules: [\n '重场景体验和关系温度,核心情绪通过场景自然流出。',\n '允许保留生活性动作和无效信息。',\n ],\n dialogueRules: [\n '人物表达自然,不用高强度戏剧句。',\n '对话保留生活气息。',\n ],\n languageRules: [\n '保留生活细节和杂音,不追求工整。',\n '口语化,句子变化中等偏高。',\n ],\n rhythmRules: [\n '慢节奏沉浸,但避免空转。',\n '允许碎片化流动。',\n ],\n defaultAntiAiRuleKeys: ['禁止段尾升华', '段落长度过于整齐', '鼓励无意义小动作'],\n },\n {\n key: 'cold-professional',\n name: '冷峻专业流',\n description: '以专业事实和行业细节压住情绪,形成克制压力感。',\n category: '专业流',\n applicableGenres: ['职场', '现实', '悬疑'],\n proseRules: [\n '行业事实和程序细节优先,情绪不直说,信息密度高于抒情密度。',\n '场景单元按「事实→动作→专业判断→后果」推进。',\n '让专业事实承担叙事重量。',\n ],\n dialogueRules: [\n '情绪藏在专业动作和事实选择里。',\n '对话以信息性表达为主,克制。',\n ],\n languageRules: [\n '术语和事实优先,避免廉价金句。',\n '正式、克制的语言。',\n ],\n rhythmRules: [\n '信息密度高,但不铺张解释。',\n '平衡节奏,段落密度中等偏高。',\n ],\n defaultAntiAiRuleKeys: ['禁止直接说教', '禁止总结主题', '句式重复率过高'],\n },\n {\n key: 'absurd-dark-humor',\n name: '荒诞黑色幽默流',\n description: '通过反差、冷感观察和荒诞细节制造黑色幽默。',\n category: '黑色幽默',\n applicableGenres: ['都市', '黑色幽默', '现实'],\n proseRules: [\n '用反差和荒诞细节放大现实困境,笑点和压迫感同时存在。',\n '场景单元按「现实细节→荒诞偏差→冷反应」推进。',\n '依赖反差和冷感观察,而非热闹吐槽。',\n ],\n dialogueRules: [\n '情绪藏在冷反应和嘴硬里。',\n '台词冷面、口语化,允许自嘲与转移。',\n ],\n languageRules: [\n '允许夹带荒诞杂质和冷幽默节奏。',\n '口语化,句子变化度高。',\n ],\n rhythmRules: [\n '反差点要快落地,不要解释笑点。',\n '平衡节奏,段落密度中等偏高。',\n ],\n defaultAntiAiRuleKeys: ['禁止解释型心理描写', '禁止段尾升华', '鼓励现实落差', '鼓励嘴硬补偿'],\n },\n]\n\n// ---------------------------------------------------------- built-in rules\n\n/** 内置全局反 AI 规则(来自 AI-Novel-Writing-Assistant 内置 DEFAULT_ANTI_AI_RULES)。 */\nexport const BUILTIN_ANTI_AI_RULES: AntiAiRule[] = [\n {\n name: '禁止解释型心理描写',\n avoid: '直接使用\"他感到\"\"他意识到\"\"他明白了\"等句式解释人物心理。',\n fix: '把心理解释改成动作、语气、停顿、环境反应或结果。',\n detectPatterns: ['他感到', '她感到', '他意识到', '她意识到', '他明白了', '她明白了'],\n builtin: true,\n },\n {\n name: '禁止段尾升华',\n avoid: '在段尾或收尾处用总结句升华主题(如\"生活就是\"\"命运总会\"\"说到底\")。',\n fix: '删除升华句,回到具体动作、现场或悬而未决的处境。',\n detectPatterns: ['生活就是', '命运总会', '归根结底', '说到底', '这就是'],\n builtin: true,\n },\n {\n name: '禁止总结主题',\n avoid: '把段落写成总结中心思想或提炼人生道理(如\"这说明\"\"这意味着\")。',\n fix: '删掉主题总结,让信息通过事件和结果自然显现。',\n detectPatterns: ['这说明', '这意味着', '归根结底', '其实就是'],\n builtin: true,\n },\n {\n name: '禁止直接说教',\n avoid: '作者替角色或读者做直接价值判断和说教(如\"我们都应该\"\"人总要学会\")。',\n fix: '改成角色具体处境或对话,不做抽象说教。',\n detectPatterns: ['我们都应该', '人总要学会', '真正重要的是'],\n builtin: true,\n },\n {\n name: '段落长度过于整齐',\n avoid: '段落长度和节奏过于平均,产生 AI 作文感。',\n fix: '打破段落长度均衡,让句子和段落有自然起伏。',\n detectPatterns: [],\n builtin: true,\n },\n {\n name: '连续三段解释性叙事',\n avoid: '连续几段只有解释没有动作,削弱现场感。',\n fix: '插入动作、对话、环境反馈,减少连段说明。',\n detectPatterns: [],\n builtin: true,\n },\n {\n name: '对话纯功能推进',\n avoid: '对话只有信息推进,没有人物语气和生活噪音(如\"告诉你\"\"我们现在要\")。',\n fix: '补入停顿、绕弯、语气差异和无效信息。',\n detectPatterns: ['告诉你', '我们现在要', '接下来就'],\n builtin: true,\n },\n {\n name: '句式重复率过高',\n avoid: '连续句式过于整齐(如\"首先\"\"然后\"\"接着\"\"最后\"),显得机械。',\n fix: '拉开句式长度和起句方式,打散结构。',\n detectPatterns: ['首先', '然后', '接着', '最后'],\n builtin: true,\n },\n {\n name: 'AI 高频套话',\n avoid: '滥用\"不禁\"\"仿佛\"\"一时间\"\"不由得\"\"顿时\"\"然而\"\"缓缓\"\"轻轻\"\"微微\"\"似乎\"\"终于\"等模式词及套路比喻。',\n fix: '用具体、有画面感的表达替换套话;每个比喻都应当是新造的。',\n detectPatterns: ['不禁', '仿佛', '一时间', '不由得', '顿时', '缓缓', '轻轻', '微微'],\n builtin: true,\n },\n {\n name: '鼓励无意义小动作',\n avoid: '(鼓励类)全篇缺少真实但不推动主线的小动作,人物显得空洞。',\n fix: '补入挠头、点烟、抠包装、挪椅子等小动作,增加人味与生活感。',\n detectPatterns: [],\n builtin: true,\n },\n {\n name: '鼓励现实落差',\n avoid: '(鼓励类)人物预期和现实结果完全一致,缺少落差。',\n fix: '补出人物预期与实际结果之间的差距,制造张力。',\n detectPatterns: [],\n builtin: true,\n },\n {\n name: '鼓励嘴硬补偿',\n avoid: '(鼓励类)人物吃瘪后没有维持体面的反应。',\n fix: '给角色补一句嘴硬找补或自我合理化,保持人设温度。',\n detectPatterns: [],\n builtin: true,\n },\n]\n\n/** 内置题材基底库(常用网文题材树,跨书复用)。 */\nexport const BUILTIN_GENRE_LIBRARY: GenreNode[] = [\n {\n name: '仙侠修真',\n description: '以修仙境界、宗门斗争、法宝丹药为核心,读者期待从凡人到强者的成长与长生问道。',\n children: [\n { name: '凡人流', description: '资质平凡、步步为营,靠资源积累与心机博弈逆袭,强调真实感与代入感。', children: [] },\n { name: '苟道流', description: '主角苟且发育、藏锋敛芒,坐收渔利,强调生存智慧与反差爽点。', children: [] },\n { name: '争霸流', description: '宗门、王朝或大陆争锋,主角由弱到强整合势力,强调格局与权谋。', children: [] },\n ],\n },\n {\n name: '都市异能',\n description: '现代都市背景叠加超能力,读者期待隐藏身份、扮猪吃虎与日常反差。',\n children: [\n { name: '异能升级', description: '觉醒超能力后不断变强,隐藏于都市,遇敌碾压。', children: [] },\n { name: '重生复仇', description: '重生回到过去,利用先知先觉改变命运、清算仇敌。', children: [] },\n { name: '商业经营', description: '以超能力或见识经商扩张,建立商业帝国,强调经营爽感。', children: [] },\n ],\n },\n {\n name: '悬疑推理',\n description: '以谜题、案件与真相揭露为核心,读者期待线索层层展开与反转。',\n children: [\n { name: '本格推理', description: '公平线索、逻辑推演,读者可与主角一同解谜。', children: [] },\n { name: '刑侦探案', description: '警察或侦探视角连续破案,案件串联主线,强调现实与人性。', children: [] },\n { name: '无限流', description: '主角穿梭于不同副本世界解谜求生,副本之间累积成长。', children: [] },\n ],\n },\n {\n name: '玄幻奇幻',\n description: '异世界或架空大陆的冒险成长,读者期待宏大世界观、奇遇与战力突破。',\n children: [\n { name: '学院流', description: '入学修炼、同窗竞争、大赛扬名,强调青春感与阶梯式打脸。', children: [] },\n { name: '废柴逆袭', description: '开局废柴受辱,觉醒金手指后一路逆袭打脸,强调反差与爽点。', children: [] },\n { name: '诸天万界', description: '穿越诸天世界收集资源与能力,强调世界多样性与成长曲线。', children: [] },\n ],\n },\n {\n name: '历史军事',\n description: '以历史时代为背景的争霸、谋略或军旅故事,读者期待权谋博弈与时代质感。',\n children: [\n { name: '王朝争霸', description: '乱世崛起、招贤纳士、逐鹿天下,强调战略与人心。', children: [] },\n { name: '穿越种田', description: '穿越古代发展生产、经营家族,强调建设感与生活细节。', children: [] },\n ],\n },\n {\n name: '末世科幻',\n description: '末世危机或科幻设定下的生存与重建,读者期待资源管理、危机升级与人性考验。',\n children: [\n { name: '基地经营', description: '建立基地、收集资源、抵御危机,强调建设与扩张。', children: [] },\n { name: '进化觉醒', description: '末世异变中觉醒能力不断进化,强调战力成长与危机求生。', children: [] },\n ],\n },\n]\n\n/** 内置常用推进模式。 */\nexport const BUILTIN_PROGRESSION_MODES: ProgressionMode[] = [\n {\n name: '升级变强',\n driver: '主角的实力、境界或能力持续增长,读者期待每次突破带来的碾压与认可。',\n readerExpectation: '每隔几章有一次明确的实力提升或打脸兑现;大境界突破要有仪式感。',\n payoffs: ['突破境界', '学会新技能', '越级战胜强敌', '当众打脸质疑者'],\n risks: ['升级重复套路', '战力膨胀失控', '无铺垫强行突破'],\n primary: false,\n },\n {\n name: '经营扩张',\n driver: '主角的产业、势力或领地不断扩张,资源复利滚雪球。',\n readerExpectation: '经营投入有可感知的回报,扩张遇到新挑战并解决。',\n payoffs: ['新产业上线', '规模翻倍', '吞并对手', '资源闭环成型'],\n risks: ['过程枯燥', '扩张无阻力', '数值失衡'],\n primary: false,\n },\n {\n name: '解谜揭露',\n driver: '主线谜团(身世、阴谋、世界观真相)持续牵引读者,每揭开一层又引出更深一层。',\n readerExpectation: '定期有真相碎片放出,回收旧伏笔、埋设新伏笔。',\n payoffs: ['伏笔回收', '身份揭露', '阴谋浮出水面', '反转打脸'],\n risks: ['谜题拖太久', '伏笔忘记回收', '反转生硬'],\n primary: false,\n },\n {\n name: '渔翁得利',\n driver: '强敌相互厮杀,主角躲在暗处观察、收割,风险由他人承担、果实由主角获取。',\n readerExpectation: '冲突升级时主角以最小代价获取最大收益,且不暴露自身。',\n payoffs: ['坐收渔利', '捡漏宝物', '敌人两败俱伤', '信息差获利'],\n risks: ['重复套路', '收割太轻易', '主角全程无风险'],\n primary: false,\n },\n {\n name: '关系拉扯',\n driver: '人物关系(知己、对手、师徒、情感线)的张力与变化持续推动剧情。',\n readerExpectation: '关系有进有退、有误会与和解,情绪起伏带动阅读欲。',\n payoffs: ['关系升温', '信任建立', '背叛与挽回', '并肩作战'],\n risks: ['情感线停滞', '工业糖精', '为虐而虐'],\n primary: false,\n },\n]\n\n// ------------------------------------------------------------- persistence\n\n/** 默认(空)项目写作资产。 */\nexport function emptyProjectAssets(): ProjectAssets {\n return {\n auxiliaryProgressions: [],\n antiAiRules: [],\n styleAssets: [],\n }\n}\n\n/** 合并项目资产与内置库:返回「生效的反 AI 规则」(内置全局 + 项目自定义)。 */\nexport function effectiveAntiAiRules(assets: ProjectAssets | undefined): AntiAiRule[] {\n const custom = assets?.antiAiRules ?? []\n const customNames = new Set(custom.map(r => r.name))\n return [...BUILTIN_ANTI_AI_RULES.filter(r => !customNames.has(r.name)), ...custom]\n}\n\n/** 把生效规则渲染成提示词块(压缩:avoid/fix 截断,省 token)。 */\nexport function renderAntiAiRules(assets: ProjectAssets | undefined): string {\n const rules = effectiveAntiAiRules(assets)\n if (rules.length === 0) return ''\n const clip = (value: string, max: number): string => value.length > max ? value.slice(0, max) + '…' : value\n return [\n '==================== 反 AI 规则(写作时必须遵守的表达边界) ====================',\n ...rules.map(r => `- ${r.name}:避免——${clip(r.avoid, 90)}${r.fix !== '' ? `;修正——${clip(r.fix, 50)}` : ''}`),\n ].join('\\n')\n}\n\n/** 渲染题材与推进模式提示词块。 */\nexport function renderGenreAndProgression(assets: ProjectAssets | undefined): string {\n const sections: string[] = []\n if (assets?.genre !== undefined) {\n sections.push('==================== 题材基底(本书的题材定位与读者期待) ====================')\n sections.push(`题材:${assets.genre.name}`)\n if (assets.genre.description !== '') sections.push(`读者期待:${assets.genre.description}`)\n const walk = (node: GenreNode, depth: number): void => {\n for (const child of node.children) {\n sections.push(`${' '.repeat(depth)}- ${child.name}:${child.description}`)\n walk(child, depth + 1)\n }\n }\n walk(assets.genre, 1)\n }\n const modes = [\n ...(assets?.primaryProgression !== undefined ? [assets.primaryProgression] : []),\n ...(assets?.auxiliaryProgressions ?? []),\n ]\n if (modes.length > 0) {\n sections.push('==================== 推进模式(读者为什么继续看) ====================')\n for (const mode of modes) {\n const tag = mode.primary ? '(主推进)' : '(辅助)'\n sections.push(`- 模式「${mode.name}」${tag}:驱动力——${mode.driver}`)\n sections.push(` 读者期待:${mode.readerExpectation}`)\n if (mode.payoffs.length > 0) sections.push(` 常见兑现:${mode.payoffs.join('、')}`)\n if (mode.risks.length > 0) sections.push(` 节奏风险(避免):${mode.risks.join('、')}`)\n }\n }\n return sections.join('\\n')\n}\n\n/** 渲染写法资产提示词块(规则去重,省 token)。 */\nexport function renderStyleAssets(assets: ProjectAssets | undefined): string {\n const styles = assets?.styleAssets ?? []\n if (styles.length === 0) return ''\n const sections: string[] = ['==================== 写法资产(本书的叙事风格约束) ====================']\n for (const style of styles) {\n sections.push(`【${style.name}】`)\n const unique = (rules: string[]): string[] => [...new Set(rules)]\n if (style.proseRules.length > 0) sections.push('叙述与节奏:\\n' + unique(style.proseRules).map(r => `- ${r}`).join('\\n'))\n if (style.dialogueRules.length > 0) sections.push('台词风格:\\n' + unique(style.dialogueRules).map(r => `- ${r}`).join('\\n'))\n if (style.descriptionRules.length > 0) sections.push('描写与情绪:\\n' + unique(style.descriptionRules).map(r => `- ${r}`).join('\\n'))\n if (style.boundaries.length > 0) sections.push('表达边界:\\n' + unique(style.boundaries).map(r => `- ${r}`).join('\\n'))\n }\n return sections.join('\\n')\n}\n\n/** 渲染全部写作资产提示词(供生成/规划/审稿注入)。 */\nexport function renderAllAssets(assets: ProjectAssets | undefined): string {\n const parts = [\n renderGenreAndProgression(assets),\n renderStyleAssets(assets),\n renderAntiAiRules(assets),\n ].filter(part => part !== '')\n return parts.join('\\n\\n')\n}\n\n/** 预置写法模板 → 可直接绑定的 StyleAsset。 */\nexport function styleTemplateToAsset(template: StyleTemplate): StyleAsset {\n return {\n name: template.name,\n proseRules: [...template.proseRules, ...template.rhythmRules.map(r => `节奏:${r}`)],\n dialogueRules: template.dialogueRules,\n descriptionRules: template.languageRules,\n boundaries: [`模板「${template.name}」适用题材:${template.applicableGenres.join('、')}`, '不要违背模板的叙事单元结构与节奏约束'],\n createdAt: new Date().toISOString(),\n }\n}\n\n// ------------------------------------------------------- style extraction\n\n/** 写法引擎:从样本文本提取风格资产的系统提示词。 */\nexport function styleEngineSystemPrompt(): string {\n return [\n '你是一位资深网文文风分析师。你会收到一段样本文本,请提炼出可复用的叙事风格规则,供后续章节保持同一种味道。',\n '要求:',\n '1. 从样本中归纳,不要泛泛而谈;每条规则都要能落到具体写法(句式、用词、视角、节奏、对话方式、描写密度)。',\n '2. 台词风格要说明角色说话的语气特征与常用表达方式。',\n '3. 表达边界要写明这段风格「不会怎么做」(如:不用华丽辞藻、不写长段心理独白、不用成语堆砌)。',\n '4. 输出必须是合法 JSON 对象,不要输出任何其他文字。',\n 'JSON 结构:',\n '{\"proseRules\": [\"叙述视角与句式节奏规则\"], \"dialogueRules\": [\"台词风格规则\"], \"descriptionRules\": [\"描写密度与情绪表达规则\"], \"boundaries\": [\"表达边界\"]}',\n ].join('\\n')\n}\n","/**\n * Novel engine — the host half's core: LLM-driven story-bible extraction,\n * volume planning, chapter planning, chapter-by-chapter writing with\n * auto-review + rewrite, polish (de-AI-ify), narrative summaries, foreshadow\n * tracking, project persistence, and whole-book export. Pure Node (no\n * web-server dependencies), so routes stay thin and logic is testable.\n */\n\nimport { mkdirSync, readFileSync, writeFileSync, readdirSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createUserMessage, BlockAssembler, ReasoningEffortId, type GenerateOptions, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { emptyProjectAssets, renderAllAssets, styleEngineSystemPrompt } from './assets.ts'\nimport type {\n AuditIssue,\n ChapterPlan,\n Foreshadow,\n NovelConfig,\n ProjectState,\n ReviewReport,\n RoleStatusCard,\n StoryBible,\n Volume,\n WorldState,\n} from './protocol.ts'\n\n/** Project state file name inside the output dir. */\nexport const PROJECT_FILE = 'novel-project.json'\n\n// ------------------------------------------------------------------ helpers\n\n/** Sanitize a file name: keep CJK/alphanumerics/space/dash/underscore. */\nfunction safeFileName(name: string): string {\n return name\n .replace(/[\\\\/:*?\"<>|]/g, '')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, 60)\n}\n\n/** Chapter output file name, e.g. 第001章_开篇.md */\nexport function chapterFileName(chapter: ChapterPlan): string {\n const title = safeFileName(chapter.title) || `第${chapter.no}章`\n return `第${String(chapter.no).padStart(3, '0')}章_${title}.md`\n}\n\n/** Infer a book name from the outline's first non-empty line. */\nexport function inferBookName(outline: string): string {\n const line = outline.split('\\n').map(l => l.trim()).find(l => l.length > 0)\n return (line ?? '未命名小说').replace(/^《/, '').replace(/》.*$/, '').slice(0, 40)\n}\n\n// ------------------------------------------------------------------ project\n\n/** Read the persisted project from the output dir (undefined when absent). */\nexport function loadProject(outputDir: string): ProjectState | undefined {\n const file = join(outputDir, PROJECT_FILE)\n if (!existsSync(file)) return undefined\n try {\n let rawText = readFileSync(file, 'utf8')\n // Tolerate a UTF-8 BOM (some editors / PowerShell writes add one).\n if (rawText.charCodeAt(0) === 0xFEFF) rawText = rawText.slice(1)\n const raw = JSON.parse(rawText) as ProjectState\n if (typeof raw.outline !== 'string' || !Array.isArray(raw.chapters)) return undefined\n // Normalize legacy projects (foreshadows / assets may be missing).\n if (!Array.isArray(raw.foreshadows)) raw.foreshadows = []\n if (raw.assets === undefined || typeof raw.assets !== 'object') raw.assets = emptyProjectAssets()\n if (!Array.isArray(raw.assets.antiAiRules)) raw.assets.antiAiRules = []\n if (!Array.isArray(raw.assets.auxiliaryProgressions)) raw.assets.auxiliaryProgressions = []\n if (!Array.isArray(raw.assets.styleAssets)) raw.assets.styleAssets = []\n if (!Array.isArray(raw.facts)) raw.facts = []\n return raw\n } catch {\n return undefined\n }\n}\n\n/** Persist the project state next to the chapters. */\nexport function saveProject(outputDir: string, project: ProjectState): void {\n mkdirSync(outputDir, { recursive: true })\n writeFileSync(join(outputDir, PROJECT_FILE), JSON.stringify(project, null, 2), 'utf8')\n}\n\n/** List generated chapter files in the output dir (sorted). */\nexport function listChapterFiles(outputDir: string): string[] {\n if (!existsSync(outputDir)) return []\n try {\n return readdirSync(outputDir)\n .filter(name => /^第\\d+章_.*\\.md$/.test(name) && !name.endsWith('.bak.md'))\n .sort((a, b) => {\n const na = Number(/^第(\\d+)章/.exec(a)?.[1] ?? 0)\n const nb = Number(/^第(\\d+)章/.exec(b)?.[1] ?? 0)\n return na - nb\n })\n } catch {\n return []\n }\n}\n\n/** Re-sync chapter status against files on disk (a file may exist without state). */\nexport function syncProjectWithDisk(project: ProjectState, outputDir: string): void {\n const files = new Map<string, string>()\n for (const file of listChapterFiles(outputDir)) {\n const no = Number(/^第(\\d+)章/.exec(file)?.[1] ?? 0)\n if (no > 0) files.set(String(no), file)\n }\n for (const chapter of project.chapters) {\n const file = files.get(String(chapter.no))\n if (file !== undefined && (chapter.status === 'pending' || chapter.status === 'generating')) {\n chapter.status = 'written'\n chapter.file = file\n }\n }\n project.updatedAt = new Date().toISOString()\n}\n\n/** Read a chapter's markdown body from disk (undefined when missing). */\nexport function readChapterFile(outputDir: string, chapter: ChapterPlan): string | undefined {\n if (chapter.file === undefined) return undefined\n const path = join(outputDir, chapter.file)\n if (!existsSync(path)) return undefined\n return readFileSync(path, 'utf8')\n}\n\n/** Create a fresh project from an outline. */\nexport function createProject(outline: string, outlinePath?: string): ProjectState {\n const now = new Date().toISOString()\n return {\n bookName: inferBookName(outline),\n outline,\n outlinePath,\n chapters: [],\n foreshadows: [],\n assets: emptyProjectAssets(),\n facts: [],\n createdAt: now,\n updatedAt: now,\n }\n}\n\n// ------------------------------------------------------------------- llm\n\n/** One complete non-streaming LLM call. */\nasync function complete(\n ctx: Context,\n config: NovelConfig,\n options: { system: string; user: string; temperature?: number; maxTokens?: number },\n): Promise<string> {\n const messages: Message[] = [createUserMessage({\n content: [{ type: 'text', text: options.user }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n })]\n const request: GenerateOptions = {\n provider: config.provider,\n model: config.model,\n messages,\n system: options.system,\n maxTokens: options.maxTokens ?? config.maxTokens,\n temperature: options.temperature ?? 0.7,\n }\n const assembler = new BlockAssembler()\n for await (const chunk of ctx.llm.stream(request)) {\n assembler.push(chunk)\n }\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n throw new Error(`LLM 调用失败(${finish.kind}): ${finish.failure.message}`)\n }\n if (finish.kind === 'max-tokens') {\n throw new Error('LLM 输出达到 maxTokens 上限,请增大配置后重试')\n }\n const blocks = assembler.blocks()\n // Diagnostics: log the assembled block shape (reasoning-only turns yield no\n // text blocks — the v4-flash model can answer entirely in the reasoning\n // channel, which the adapter surfaces as a reasoning block).\n if (process.env.DSH_NOVEL_DEBUG === '1') {\n console.error('[dsh-novel-forge] complete: finish=%j blocks=%j', JSON.stringify(finish), blocks.map(b => `${b.type}:${'text' in b ? b.text.length : '?'}`))\n }\n const textBlocks = blocks\n .filter((block): block is Extract<StreamChunk, { type: 'block-end' }>['block'] & { type: 'text' } => block.type === 'text')\n .map(block => block.text)\n let text = textBlocks.join('\\n').trim()\n // v4-flash can answer entirely in the reasoning channel (the adapter\n // surfaces that as a 'reasoning' block). Fall back to it when no text came\n // back — the reasoning content is the model's actual answer here.\n if (text === '') {\n const reasoning = blocks\n .filter((block): block is { type: 'reasoning'; text: string } => block.type === 'reasoning')\n .map(block => block.text)\n .join('\\n')\n .trim()\n if (reasoning !== '') text = reasoning\n }\n return text\n}\n\n/**\n * Parse a JSON value out of a model response. Multi-level tolerance because\n * models are sloppy: prose around the JSON, ```json fences, a truncated tail,\n * or raw newlines inside string values all defeat a single JSON.parse. We\n * walk candidates from strictest to loosest.\n */\nfunction parseJson<T>(text: string, wantArray: boolean): T {\n const candidates: string[] = []\n const push = (value: string | undefined): void => {\n if (value !== undefined && value.trim() !== '') candidates.push(value.trim())\n }\n\n // 1. Whole response, and any ```json fence body.\n push(text)\n const fenced = /```(?:json)?\\s*([\\s\\S]*?)```/.exec(text)\n push(fenced?.[1])\n // 2. From the first opener to the last closer.\n const opener = wantArray ? '[' : '{'\n const closer = wantArray ? ']' : '}'\n const start = text.indexOf(opener)\n const end = text.lastIndexOf(closer)\n if (start !== -1 && end > start) push(text.slice(start, end + 1))\n // 3. Trim trailing prose (a \"}...\" tail after the last closer).\n const trimmed = text.replace(new RegExp(`${closer}[\\\\s\\\\S]*$`), closer)\n push(trimmed)\n const start2 = trimmed.indexOf(opener)\n if (start2 !== -1) push(trimmed.slice(start2))\n\n // Repair: models love raw newlines inside string values, which JSON forbids.\n const repair = (value: string): string => {\n let out = ''\n let inString = false\n for (let i = 0; i < value.length; i++) {\n const ch = value[i]!\n if (inString) {\n if (ch === '\\\\') {\n out += ch + (value[i + 1] ?? '')\n i++\n continue\n }\n if (ch === '\"') {\n inString = false\n out += ch\n continue\n }\n if (ch === '\\n' || ch === '\\r') {\n out += '\\\\n'\n continue\n }\n out += ch\n } else {\n if (ch === '\"') inString = true\n out += ch\n }\n }\n return out\n }\n\n for (const candidate of candidates) {\n for (const attempt of [candidate, repair(candidate)]) {\n try {\n const value = JSON.parse(attempt) as unknown\n if (!wantArray || Array.isArray(value)) return value as T\n // wantArray but the model wrapped the list in an object, e.g.\n // {\"chapters\": [...]} — extract the first array-valued key.\n if (typeof value === 'object' && value !== null) {\n for (const key of Object.keys(value as Record<string, unknown>)) {\n const inner = (value as Record<string, unknown>)[key]\n if (Array.isArray(inner)) return inner as T\n }\n }\n // Not an array — keep trying the remaining candidates.\n } catch {\n // try the next candidate\n }\n }\n }\n const preview = text.length > 300 ? text.slice(0, 300) + '…' : text\n throw new Error(`模型输出中未找到 JSON 数据。模型原始输出:${preview}`)\n}\n\n/** Parse a JSON array (chapters, volumes, issues...). */\nfunction parseJsonArray<T>(text: string): T[] {\n const value = parseJson<T[]>(text, true)\n return Array.isArray(value) ? value : []\n}\n\n/** Parse a JSON object. */\nfunction parseJsonObject<T>(text: string): T {\n const value = parseJson<T>(text, false)\n if (typeof value !== 'object' || value === null) throw new Error('模型输出不是 JSON 对象')\n return value\n}\n\n// ------------------------------------------------------------------ bible\n\n/** System prompt for story-bible extraction. */\nfunction bibleSystemPrompt(): string {\n return [\n '你是一位资深网文编辑兼设定架构师。你会收到一份小说大纲,请把它提炼成结构化的「设定圣经」(Story Bible),供后续写作时严格引用。',\n '要求:',\n '1. 忠于大纲,不自行发明大纲之外的设定。',\n '2. 角色卡覆盖大纲明确出现的角色(主角必含),每个角色给出性格标签、目标、关键关系。',\n '3. 世界规则覆盖力量体系、金手指机制、势力、地理等所有硬性规则,逐条列出。',\n '4. 红线列出大纲中明确禁止的内容(如无后宫、不圣母、无无脑碾压等)。',\n '5. 风格列出叙事基调、节奏、POV 等写作风格要点。',\n '输出必须是合法 JSON 对象,不要输出任何其他文字或 Markdown 代码块标记。',\n '重要:所有字符串值内部不得包含换行符(不要用多行字符串),JSON 必须在一段内完整结束。',\n '重要:直接输出 JSON 结果本身,不要把思考过程或推理内容写在输出里。',\n 'JSON 结构:',\n '{\"genre\": \"题材与基调一句话\", \"worldRules\": [\"规则1\", \"规则2\", ...], \"characters\": [{\"name\": \"角色名\", \"role\": \"protagonist|supporting|antagonist|other\", \"traits\": [\"标签1\", ...], \"goals\": \"目标与动机\", \"relations\": \"关键关系\"}], \"redLines\": [\"红线1\", ...], \"style\": [\"风格1\", ...]}',\n ].join('\\n')\n}\n\n/** Extract the story bible from an outline. */\nexport async function extractBible(ctx: Context, config: NovelConfig, outline: string): Promise<StoryBible> {\n const user = `请为下面这部小说提炼设定圣经:\\n\\n${outline}`\n const text = await complete(ctx, config, {\n system: bibleSystemPrompt(),\n user,\n temperature: 0.4,\n maxTokens: Math.max(config.maxTokens, 16000),\n })\n const raw = parseJsonObject<{\n genre?: unknown\n worldRules?: unknown\n characters?: unknown\n redLines?: unknown\n style?: unknown\n }>(text)\n const strArray = (value: unknown): string[] =>\n Array.isArray(value) ? value.filter((v): v is string => typeof v === 'string' && v.trim() !== '') : []\n const characters: StoryBible['characters'] = Array.isArray(raw.characters)\n ? raw.characters\n .filter((v): v is Record<string, unknown> => typeof v === 'object' && v !== null)\n .map(entry => ({\n name: typeof entry.name === 'string' ? entry.name.trim() : '未命名',\n role: (['protagonist', 'supporting', 'antagonist', 'other'] as const).includes(entry.role as never)\n ? entry.role as StoryBible['characters'][number]['role']\n : 'other',\n traits: strArray(entry.traits),\n goals: typeof entry.goals === 'string' ? entry.goals : '',\n relations: typeof entry.relations === 'string' ? entry.relations : '',\n }))\n .filter(card => card.name !== '')\n : []\n const bible: StoryBible = {\n genre: typeof raw.genre === 'string' ? raw.genre : '',\n worldRules: strArray(raw.worldRules),\n characters,\n redLines: strArray(raw.redLines),\n style: strArray(raw.style),\n generatedAt: new Date().toISOString(),\n }\n if (bible.worldRules.length === 0 && bible.characters.length === 0 && bible.redLines.length === 0) {\n throw new Error('道藏生成失败:模型没有返回有效内容')\n }\n return bible\n}\n\n// ------------------------------------------------------------------ volumes\n\n/** System prompt for volume planning. */\nfunction volumeSystemPrompt(): string {\n return [\n '你是一位资深网文总编。你会收到一份小说大纲,请把全书划分为若干「卷」(分卷),每卷有明确的剧情定位与起止章节。',\n '要求:',\n '1. 大纲已有分卷时,严格遵循大纲的分卷结构;没有时按剧情弧线合理划分(3-8 卷)。',\n '2. 卷定位一句话说明该卷的剧情重心。',\n '3. chapterStart/chapterEnd 给出该卷覆盖的章节区间(从 1 开始连续编号)。',\n '输出必须是合法 JSON 数组,不要输出任何其他文字:',\n '[{\"no\": 1, \"title\": \"卷名\", \"summary\": \"卷定位与剧情重心\", \"chapterStart\": 1, \"chapterEnd\": 80}]',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n '重要:直接输出 JSON 结果本身,不要把思考过程或推理内容写在输出里。',\n ].join('\\n')\n}\n\n/** Plan volumes from an outline. */\nexport async function planVolumes(ctx: Context, config: NovelConfig, outline: string): Promise<Volume[]> {\n const user = `请为下面这部小说划分卷:\\n\\n${outline}`\n const text = await complete(ctx, config, { system: volumeSystemPrompt(), user, temperature: 0.4, maxTokens: Math.max(config.maxTokens, 12000) })\n const parsed = parseJsonArray<Record<string, unknown>>(text)\n const volumes: Volume[] = []\n for (let i = 0; i < parsed.length; i++) {\n const entry = parsed[i]\n if (typeof entry !== 'object' || entry === null) continue\n const no = typeof entry.no === 'number' ? entry.no : i + 1\n const title = typeof entry.title === 'string' ? entry.title.trim() : `第${no}卷`\n const summary = typeof entry.summary === 'string' ? entry.summary.trim() : ''\n const start = typeof entry.chapterStart === 'number' ? entry.chapterStart : undefined\n const end = typeof entry.chapterEnd === 'number' ? entry.chapterEnd : undefined\n volumes.push({\n no,\n title: title.slice(0, 40),\n summary: summary.slice(0, 300),\n chapterStart: start ?? 1,\n chapterEnd: end ?? 1,\n })\n }\n if (volumes.length === 0) throw new Error('卷计划生成失败:模型没有返回有效卷')\n return volumes\n}\n\n/** Assign a chapter to its volume by number. */\nfunction volumeOf(chapterNo: number, volumes: Volume[] | undefined): number {\n if (volumes === undefined || volumes.length === 0) return 0\n for (const volume of volumes) {\n if (chapterNo >= volume.chapterStart && chapterNo <= volume.chapterEnd) return volume.no\n }\n return volumes[volumes.length - 1]?.no ?? 0\n}\n\n// ------------------------------------------------------------------- plan\n\n/** The chapter-planning prompt template. */\nfunction planSystemPrompt(volumes: Volume[] | undefined): string {\n const volumeBlock = volumes !== undefined && volumes.length > 0\n ? ['\\n全书分卷结构(规划章节时需落在对应卷内):']\n .concat(volumes.map(v => `第${v.no}卷《${v.title}》:${v.summary}(章节 ${v.chapterStart}-${v.chapterEnd})`))\n .join('\\n')\n : ''\n return [\n '你是一位资深中文网文策划编辑,擅长把小说大纲拆解为可执行的章节计划。',\n '你会收到一份小说大纲。请根据大纲的设定、主线与节奏,规划出一份章节计划。',\n '要求:',\n '1. 每章必须有明确的核心剧情推进(不能只是过渡或凑字数)。',\n '2. 章节之间要衔接自然,前章结尾为后章埋下钩子。',\n '3. 严格遵循大纲的人设、金手指规则、战力体系与世界观设定,不得自行发明冲突设定。',\n '4. 输出必须是合法的 JSON 数组,不要输出任何其他文字或 Markdown 代码块标记。',\n '5. 数组每个元素格式:{\"title\": \"章节标题(10字以内,有网文感)\", \"beats\": \"结构化剧情要点(150-250字,必须包含四段,段间用换行分隔):\\\\n本章目标:本章要完成的核心推进;\\\\n剧情要点:主要情节的起承转合(2-4 句);\\\\n爽点/钩子:本章的爽点兑现或情绪钩子;\\\\n结尾钩子:本章结尾为下一章埋下的悬念\"}',\n '重要:beats 字段内部必须使用 \\\\n 转义表示换行(JSON 字符串内不得有真实换行符),其余字符串值也不得包含真实换行符,JSON 必须在一段内完整结束。',\n '重要:直接输出 JSON 结果本身,不要把思考过程或推理内容写在输出里。',\n volumeBlock,\n ].join('\\n')\n}\n\n/** Build the writing system prompt (bible + outline + active foreshadows). */\nfunction writeSystemPrompt(project: ProjectState): string {\n const bible = project.bible\n const sections: string[] = []\n if (bible !== undefined) {\n sections.push('==================== 设定圣经(写作时严格遵守) ====================')\n if (bible.genre !== '') sections.push(`题材基调:${bible.genre}`)\n if (bible.worldRules.length > 0) sections.push('世界规则:\\n' + bible.worldRules.map(r => `- ${r}`).join('\\n'))\n if (bible.characters.length > 0) {\n sections.push('角色卡:')\n for (const card of bible.characters) {\n const roleName = { protagonist: '主角', supporting: '配角', antagonist: '反派', other: '其他' }[card.role]\n sections.push(`- ${card.name}(${roleName}):${card.traits.join('、')}${card.goals !== '' ? `;目标:${card.goals}` : ''}${card.relations !== '' ? `;关系:${card.relations}` : ''}`)\n }\n }\n if (bible.redLines.length > 0) sections.push('写作红线(违反即失败):\\n' + bible.redLines.map(r => `- ${r}`).join('\\n'))\n if (bible.style.length > 0) sections.push('风格要求:\\n' + bible.style.map(r => `- ${r}`).join('\\n'))\n }\n const worldBlock = renderWorld(project.world)\n if (worldBlock !== '') sections.push(worldBlock)\n sections.push('==================== 全书大纲 ====================')\n // 超长大纲截断保护(防止上下文超限);完整大纲在总纲页查看。\n const outlineBlock = project.outline.length > 6000\n ? project.outline.slice(0, 6000) + '\\n…(大纲过长已节选,完整内容见总纲页)'\n : project.outline\n sections.push(outlineBlock)\n sections.push('==================== 大纲结束 ====================')\n const assetsBlock = renderAllAssets(project.assets)\n if (assetsBlock !== '') sections.push(assetsBlock)\n const active = project.foreshadows.filter(f => f.status === 'planted' || f.status === 'progressing')\n if (active.length > 0) {\n sections.push('==================== 活跃伏笔(近期需推进或回收的线索) ====================')\n for (const f of active) {\n sections.push(`- [${f.status === 'planted' ? '已埋设' : '推进中'}] ${f.description}${f.targetChapter !== undefined ? `(预计 ${f.targetChapter} 章回收)` : ''}`)\n }\n }\n sections.push('')\n sections.push('写作硬性要求:')\n sections.push('1. 每章 3000-4000 字(按中文字符计),只输出章节正文,不要输出标题、章回名、作者的话或任何 Markdown 标记。')\n sections.push('2. 以主角视角展开,动作、对话、心理描写交替推进,禁止大段设定说明。')\n sections.push('3. 尊重大纲与设定圣经:人设不崩、金手指规则不自相矛盾、战力不随意膨胀。')\n sections.push('4. 章末留一个钩子(悬念、反转或新线索),吸引读者读下一章。')\n sections.push('5. 语言流畅自然,符合中文网文语感,避免翻译腔与病句。')\n return sections.join('\\n')\n}\n\n/**\n * Plan chapters from an outline (optionally for one volume).\n */\nexport async function planChapters(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n chapterCount: number,\n volumeNo?: number,\n outputDir?: string,\n): Promise<ChapterPlan[]> {\n const volume = project.volumes?.find(v => v.no === volumeNo)\n const existing = project.chapters\n const startNo = existing.length === 0 ? 1 : Math.max(...existing.map(c => c.no)) + 1\n const continuation = existing.length > 0\n const latestFacts = continuation && Array.isArray(project.facts)\n ? project.facts.slice(-15).map(f => `[第${f.chapterNo}章] ${f.text.slice(0, 150)}`).join('\\n')\n : ''\n // 上一章(已写章节中章号最大者)结尾原文,作为续写剧情起点。\n let prevTail = ''\n if (continuation) {\n const written = existing.filter(c => c.status !== 'pending')\n const last = written[written.length - 1]\n if (last !== undefined && last.file !== undefined && outputDir !== undefined) {\n try {\n const raw = readFileSync(join(outputDir, last.file), 'utf8')\n prevTail = raw.replace(/^#.*$/m, '').trim().slice(-600)\n } catch { /* 文件缺失时忽略,仅依赖编年录 */ }\n }\n }\n // 续写模式下大纲截断到「关键剧情桥段」之前:只保留设定(人设/金手指/规则/战力),\n // 去掉分卷主线步骤模板,避免模型照抄已完成的剧情再走一遍。\n const outlineBlock = continuation\n ? (() => {\n const cut = project.outline.indexOf('七、关键剧情桥段')\n if (cut > 1500) return project.outline.slice(0, cut).trimEnd() + '\\n(大纲后续剧情桥段与分卷细节从略;续写请以「上一章结尾原文」与「最新剧情状态」为剧情起点)'\n return project.outline.slice(0, 3000) + '\\n…(大纲过长已节选)'\n })()\n : project.outline\n const user = [\n '请为下面这部小说规划章节。',\n volume !== undefined\n ? `本次只规划第 ${volume.no} 卷《${volume.title}》的章节:\\n${volume.summary}`\n : continuation\n ? `本书已有 ${existing.length} 章已规划/已写作(见下方「已有章节」)。请规划**后续**章节:从第 ${startNo} 章开始。`\n : '请规划全书开篇章节。',\n continuation\n ? '【续写硬性要求】已有章节的剧情不得重写或重复,章节标题也不得与已有章节重复。以下情节均已在已有章节中发生过,后续章节**绝对不得再次出现**:穿越、暴雨送餐、滴血认主/古玉认主、首次进入墟境、用废铁淬炼首件法器、绝境肉身入鼎洗炼(该机缘已用尽)、杀死白袍弟子与灰衣随从、藏尸水沟。'\n : '',\n prevTail !== ''\n ? `【上一章(第 ${startNo - 1} 章)结尾原文】第 ${startNo} 章必须紧接此状态继续,从新的事件写起,不得回顾重述:\\n${prevTail}`\n : '',\n latestFacts !== ''\n ? `【最新剧情状态(本书编年录,第 ${startNo - 1} 章结尾的事实)】规划续写时必须以此为起点,时间线、人物状态与地点衔接一致:\\n${latestFacts}`\n : '',\n continuation\n ? '已有章节:\\n'\n + existing.map(c => {\n const sm = c.summary !== undefined && c.summary !== '' ? `(${c.summary.slice(0, 120)})` : ''\n return `第${c.no}章《${c.title}》${sm}`\n }).join('\\n')\n : '',\n `全书大纲(设定参考,续写剧情不得与设定冲突):\\n${outlineBlock}`,\n '',\n `请规划 ${chapterCount} 章。输出 JSON 数组(不要输出其他文字):`,\n ].join('\\n')\n const system = planSystemPrompt(project.volumes) + (continuation\n ? '\\n重要:本次是**续写规划**——已有章节的剧情不得重写或重复,新章节标题不得与已有章节标题相同,新章节的剧情必须从上一章结尾自然接续(人物状态、时间线、地点衔接一致)。'\n : '')\n const text = await complete(ctx, config, { system, user, temperature: 0.7, maxTokens: Math.max(config.maxTokens, 40000) })\n const parsed = parseJsonArray<Record<string, unknown>>(text)\n const chapters: ChapterPlan[] = []\n const existingNos = new Set(existing.map(c => c.no))\n const existingTitles = new Set(existing.map(c => c.title))\n let cursor = startNo\n for (const item of parsed) {\n if (chapters.length >= chapterCount) break\n if (typeof item !== 'object' || item === null) continue\n const entry = item as Record<string, unknown>\n const title = typeof entry.title === 'string' ? entry.title.trim().slice(0, 30) : ''\n const beats = typeof entry.beats === 'string' ? entry.beats.trim() : ''\n if (title === '' && beats === '') continue\n // 续写模式下,标题与已有章节重复的一律丢弃(模型可能复述旧章节)。\n if (title !== '' && existingTitles.has(title)) continue\n while (existingNos.has(cursor)) cursor++\n const no = cursor++\n chapters.push({\n no,\n volume: volumeOf(no, project.volumes),\n title: title || `第${no}章`,\n beats,\n targetChars: config.chapterChars,\n status: 'pending',\n })\n }\n if (chapters.length === 0) {\n throw new Error('章节计划生成失败:模型没有返回有效章节')\n }\n return chapters\n}\n\n// ------------------------------------------------------------------ writing\n\n/** The review system prompt. */\nfunction reviewSystemPrompt(project: ProjectState): string {\n const bible = project.bible\n const sections: string[] = [\n '你是一位严格的网文审稿编辑。你会收到一章正文以及本书的设定圣经与红线。',\n '请从以下维度审查本章:',\n '1. 人设一致性:角色行为是否符合角色卡(主角不圣母、不无脑、痞坏有分寸等)。',\n '2. 设定一致性:金手指规则、战力体系、世界观是否与设定圣经冲突。',\n '3. 红线检查:是否触犯写作红线(无后宫、无擦边、无无脑碾压等)。',\n '4. 文笔质量:语病、翻译腔、AI 套话(\"不禁\"\"仿佛\"\"一时间\"等高频词滥用)、流水账。',\n '5. 节奏与爽点:本章是否有推进、有钩子,是否拖沓灌水。',\n '6. 逻辑漏洞:前后矛盾、时间线错误、对话失真。',\n '7. 反 AI 规则:逐条核对下方「反 AI 规则」清单,命中即列为问题。',\n '输出必须是合法 JSON 对象,不要输出任何其他文字:',\n '{\"score\": 0-100的整数, \"verdict\": \"一句话总评\", \"issues\": [{\"severity\": \"high|medium|low\", \"item\": \"问题描述\", \"suggestion\": \"修改建议\"}]}',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n '重要:直接输出 JSON 结果本身,不要把思考过程写在输出里。',\n ]\n const assetsBlock = renderAllAssets(project.assets)\n if (assetsBlock !== '') sections.push('\\n' + assetsBlock)\n if (bible !== undefined) {\n sections.push('\\n==================== 设定圣经 ====================')\n if (bible.worldRules.length > 0) sections.push('世界规则:\\n' + bible.worldRules.map(r => `- ${r}`).join('\\n'))\n if (bible.characters.length > 0) {\n sections.push('角色卡:')\n for (const card of bible.characters) {\n sections.push(`- ${card.name}(${card.role}):${card.traits.join('、')}`)\n }\n }\n if (bible.redLines.length > 0) sections.push('红线:\\n' + bible.redLines.map(r => `- ${r}`).join('\\n'))\n }\n return sections.join('\\n')\n}\n\n/** Run the AI review on one chapter. */\nexport async function reviewChapter(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): Promise<ReviewReport> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) throw new Error(`章节 ${chapterNo} 不在计划中`)\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) throw new Error(`章节 ${chapterNo} 的正文文件不存在`)\n const user = [\n `本章标题:《${chapter.title}》`,\n `本章剧情要点:${chapter.beats}`,\n '==================== 章节正文 ====================',\n body.replace(/^#\\s+.*$/m, '').trim(),\n ].join('\\n')\n const text = await complete(ctx, config, { system: reviewSystemPrompt(project), user, temperature: 0.3, maxTokens: Math.max(config.maxTokens, 16000) })\n const raw = parseJsonObject<{ score?: unknown; verdict?: unknown; issues?: unknown }>(text)\n const issues = Array.isArray(raw.issues)\n ? raw.issues\n .filter((v): v is Record<string, unknown> => typeof v === 'object' && v !== null)\n .map(entry => ({\n severity: (['high', 'medium', 'low'] as const).includes(entry.severity as never)\n ? entry.severity as 'high' | 'medium' | 'low'\n : 'medium',\n item: typeof entry.item === 'string' ? entry.item : '',\n suggestion: typeof entry.suggestion === 'string' ? entry.suggestion : '',\n }))\n .filter(issue => issue.item !== '')\n : []\n const score = typeof raw.score === 'number' ? Math.max(0, Math.min(100, Math.round(raw.score))) : 60\n const report: ReviewReport = {\n score,\n passed: score >= config.reviewPassScore,\n verdict: typeof raw.verdict === 'string' ? raw.verdict.slice(0, 200) : '',\n issues,\n reviewedAt: new Date().toISOString(),\n }\n chapter.review = report\n chapter.status = report.passed ? 'approved' : 'rejected'\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return report\n}\n\n/**\n * 审查「任意正文文本」(作者手动编辑后的草稿,不落盘)。\n * 复用审稿提示词与红线/道藏/反AI规则;仅返回报告,不改文件不改状态。\n */\nexport async function reviewChapterText(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n text: string,\n): Promise<ReviewReport> {\n const user = [\n `书名:《${project.bookName}》`,\n '==================== 待审查正文 ====================',\n text.slice(0, 20000),\n ].join('\\n')\n const raw = parseJsonObject<{ score?: unknown; verdict?: unknown; issues?: unknown }>(\n await complete(ctx, config, { system: reviewSystemPrompt(project), user, temperature: 0.3, maxTokens: Math.max(config.maxTokens, 8000) }),\n )\n const issues = Array.isArray(raw.issues)\n ? raw.issues\n .filter((v): v is Record<string, unknown> => typeof v === 'object' && v !== null)\n .map(entry => ({\n severity: (['high', 'medium', 'low'] as const).includes(entry.severity as never)\n ? entry.severity as 'high' | 'medium' | 'low'\n : 'medium',\n item: typeof entry.item === 'string' ? entry.item : '',\n suggestion: typeof entry.suggestion === 'string' ? entry.suggestion : '',\n }))\n .filter(issue => issue.item !== '')\n : []\n const score = typeof raw.score === 'number' ? Math.max(0, Math.min(100, Math.round(raw.score))) : 60\n return {\n score,\n passed: score >= config.reviewPassScore,\n verdict: typeof raw.verdict === 'string' ? raw.verdict.slice(0, 200) : '',\n issues,\n reviewedAt: new Date().toISOString(),\n }\n}\n\n/** Build the rewrite system prompt (fix review issues / instructions). */\nfunction rewriteSystemPrompt(project: ProjectState): string {\n const base = writeSystemPrompt(project)\n return base + '\\n\\n额外要求:你正在【修订】一章已写好的正文。保留原文中好的部分,只修改需要修改的地方,输出完整的新正文(不要只输出修改片段),字数与原文相当。'\n}\n\n/**\n * Stream a chapter rewrite. With `target` (a passage of the body), only that\n * passage's paragraph is rewritten and spliced back — everything else stays\n * untouched (local revision). Without `target`, the whole chapter is\n * rewritten. Yields delta text; persists when done.\n */\nexport async function* rewriteChapterStream(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n instructions: string,\n target?: string,\n): AsyncGenerator<{ frame: 'start' } | { frame: 'delta'; text: string } | { frame: 'drafted'; chars: number; draft: string }, void, unknown> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) throw new Error(`章节 ${chapterNo} 不在计划中`)\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) throw new Error(`章节 ${chapterNo} 的正文文件不存在`)\n\n const reviewBlock = chapter.review !== undefined\n ? '审稿意见:\\n' + chapter.review.issues.map(i => `[${i.severity}] ${i.item} → ${i.suggestion}`).join('\\n')\n : ''\n\n // Local revision: find the paragraph containing `target` and only rewrite it.\n const bodyText = body.replace(/^#\\s+.*$/m, '').trim()\n let localTarget: { paragraph: string; before: string; after: string } | undefined\n if (target !== undefined && target.trim() !== '') {\n const wanted = target.trim()\n // Normalize whitespace so multi-line / quoted snippets still match:\n // the assistant often copies a passage with line breaks and quotes.\n const normalize = (value: string): string => value.replace(/\\s+/g, ' ').replace(/[“”\"'‘’]/g, '')\n const wantedFlat = normalize(wanted)\n // Split into paragraphs on blank lines (or double newlines).\n const paragraphs = bodyText.split(/\\n{2,}/)\n const idx = paragraphs.findIndex(p => normalize(p).includes(wantedFlat))\n if (idx === -1) {\n throw new Error(`在正文中未找到要修改的片段:「${wanted.slice(0, 40)}…」。请从正文中复制原文片段(无需整段,取片段即可)。`)\n }\n localTarget = {\n paragraph: paragraphs[idx]!,\n before: paragraphs.slice(0, idx).join('\\n\\n'),\n after: paragraphs.slice(idx + 1).join('\\n\\n'),\n }\n }\n\n const user = localTarget === undefined\n ? [\n `请修订第 ${chapter.no} 章《${chapter.title}》。`,\n reviewBlock,\n instructions !== '' ? `本次修订重点:${instructions}` : '',\n '==================== 原正文 ====================',\n bodyText,\n ].filter(line => line !== '').join('\\n')\n : [\n `请修订第 ${chapter.no} 章《${chapter.title}》中的一个自然段。`,\n instructions !== '' ? `修改要求:${instructions}` : '',\n '==================== 需要修改的原文段落 ====================',\n localTarget.paragraph,\n '',\n '要求:',\n '1. 只输出修改后的【这一个段落】的完整新文本,不要输出任何说明、标题或 Markdown 标记。',\n '2. 保留该段的情节走向与角色口吻,只按修改要求调整。',\n '3. 段落长度与原文相当。',\n ].filter(line => line !== '').join('\\n')\n\n const system = localTarget === undefined\n ? rewriteSystemPrompt(project)\n : '你是一位中文网文润色师。你会收到一章中的一个段落,请按修改要求重写该段。只输出新段落文本。'\n\n const messages: Message[] = [createUserMessage({\n content: [{ type: 'text', text: user }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n })]\n const request: GenerateOptions = {\n provider: config.provider,\n model: config.model,\n messages,\n system,\n // Rewriting outputs a full chapter: budget generously and skip\n // reasoning (a transform task) so the whole budget goes to the body.\n maxTokens: Math.max(config.maxTokens, 20000),\n temperature: 0.7,\n reasoningEffort: ReasoningEffortId('off'),\n }\n\n yield { frame: 'start' }\n const assembler = new BlockAssembler()\n let streamError: Error | undefined\n for await (const chunk of ctx.llm.stream(request)) {\n assembler.push(chunk)\n if (chunk.type === 'text-delta') yield { frame: 'delta', text: chunk.text }\n }\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n streamError = new Error(`修订失败(${finish.kind}): ${finish.failure.message}`)\n } else if (finish.kind === 'max-tokens') {\n streamError = new Error('修订输出达到 maxTokens 上限,请增大配置后重试')\n }\n const rewritten = assembler\n .blocks()\n .filter((block): block is Extract<StreamChunk, { type: 'block-end' }>['block'] & { type: 'text' } => block.type === 'text')\n .map(block => block.text)\n .join('\\n')\n .trim()\n if (streamError !== undefined) throw streamError\n if (rewritten.length < 20) throw new Error('修订结果过短,可能失败,请重试')\n\n // Splice: local -> replace the paragraph; whole -> replace the body.\n let newBody: string\n if (localTarget !== undefined) {\n newBody = [localTarget.before, rewritten, localTarget.after].filter(part => part !== '').join('\\n\\n')\n } else {\n newBody = rewritten\n }\n if (newBody.length < 100) throw new Error('修订结果过短,可能失败,请重试')\n\n // Draft mode: do NOT overwrite the file yet. Store the new body as a\n // pending draft; the user reviews the diff and decides to apply or\n // discard. File overwrite + status change happen on draft/apply.\n chapter.pendingDraft = newBody\n chapter.error = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n yield { frame: 'drafted', chars: newBody.length, draft: newBody }\n}\n\n/** The de-AI-ify polish system prompt (with project writing assets injected). */\nfunction polishSystemPrompt(project: ProjectState): string {\n const assetsBlock = renderAllAssets(project.assets)\n return [\n '你是一位中文网文润色师。你会收到一章正文,请做「去 AI 味」润色:',\n '1. 删除/替换 AI 高频套话与模式词:如\"不禁\"\"仿佛\"\"一时间\"\"不由得\"\"顿时\"\"然而\"\"缓缓\"\"轻轻\"\"微微\"\"默默\"\"似乎\"\"终于\"等滥用。',\n '2. 把书面翻译腔改成口语化的中文网文语感。',\n '3. 拆分过长的排比句与堆砌的修饰语。',\n '4. 保留全部情节、人物、对话内容不变,只改表达。',\n '5. 输出完整的新正文,不要输出任何说明文字或 Markdown 标记。',\n '6. 必须遵守下方「反 AI 规则」与「写法资产」的表达边界;写法资产要求保留的风格特征(句式、台词、节奏)不得在润色中丢失。',\n assetsBlock !== '' ? assetsBlock : '',\n ].join('\\n')\n}\n\n/** Stream a chapter polish (de-AI-ify). Draft-mode: the polished body lands\n * in `chapter.pendingDraft` and is only applied on draft/apply. */\nexport async function* polishChapterStream(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): AsyncGenerator<{ frame: 'start' } | { frame: 'delta'; text: string } | { frame: 'drafted'; chars: number; draft: string }, void, unknown> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) throw new Error(`章节 ${chapterNo} 不在计划中`)\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) throw new Error(`章节 ${chapterNo} 的正文文件不存在`)\n const messages: Message[] = [createUserMessage({\n content: [{ type: 'text', text: body.replace(/^#\\s+.*$/m, '').trim() }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n })]\n const request: GenerateOptions = {\n provider: config.provider,\n model: config.model,\n messages,\n system: polishSystemPrompt(project),\n // Polish rewrites the whole chapter: generous budget, no reasoning\n // (transform task — the entire budget should go to the body).\n maxTokens: Math.max(config.maxTokens, 20000),\n temperature: 0.5,\n reasoningEffort: ReasoningEffortId('off'),\n }\n yield { frame: 'start' }\n const assembler = new BlockAssembler()\n let streamError: Error | undefined\n for await (const chunk of ctx.llm.stream(request)) {\n assembler.push(chunk)\n if (chunk.type === 'text-delta') yield { frame: 'delta', text: chunk.text }\n }\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n streamError = new Error(`润色失败(${finish.kind}): ${finish.failure.message}`)\n } else if (finish.kind === 'max-tokens') {\n streamError = new Error('润色输出达到 maxTokens 上限')\n }\n const newBody = assembler\n .blocks()\n .filter((block): block is Extract<StreamChunk, { type: 'block-end' }>['block'] & { type: 'text' } => block.type === 'text')\n .map(block => block.text)\n .join('\\n')\n .trim()\n if (streamError !== undefined) throw streamError\n if (newBody.length < 100) throw new Error('润色结果过短,可能失败,请重试')\n\n // Draft mode: keep the original file untouched until the user decides.\n chapter.pendingDraft = newBody\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n yield { frame: 'drafted', chars: newBody.length, draft: newBody }\n}\n\n/** Generate one chapter (streaming). Yields progress frames; persists when done. */\nexport async function* generateChapterStream(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): AsyncGenerator<{ frame: 'start' } | { frame: 'delta'; text: string } | { frame: 'done'; file: string; chars: number }, void, unknown> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) throw new Error(`章节 ${chapterNo} 不在计划中`)\n // Note: the route layer owns the 'generating' status + concurrency guard;\n // this function must not refuse when status is 'generating' (the route sets\n // it before calling us).\n\n // Continuity: previous chapter's ending + its summary (narrative memory).\n let continuity = ''\n const prev = project.chapters.find(c => c.no === chapterNo - 1)\n if (prev?.file !== undefined) {\n const prevPath = join(outputDir, prev.file)\n if (existsSync(prevPath)) {\n const text = readFileSync(prevPath, 'utf8')\n continuity = text.slice(-900)\n }\n }\n const prevSummary = prev?.summary\n // 事实注入:最近 20 条 + 按本章剧情要点低成本检索的「相关旧事实」。\n // 长篇后旧设定可能被挤出最近 20 条,相关检索保证关键状态不写飞。\n const allFacts = project.facts ?? []\n const recentFacts = allFacts.slice(-20).map(f => f.text)\n const beatsText = chapter.beats\n const relatedFacts = allFacts\n .slice(-120)\n .filter(f => {\n const head = f.text.slice(0, 24)\n for (let i = 0; i + 3 <= head.length; i++) {\n const tri = head.slice(i, i + 3)\n if (tri.trim() !== '' && beatsText.includes(tri)) return true\n }\n return false\n })\n .slice(-15)\n .map(f => `[第${f.chapterNo}章] ${f.text}`)\n\n const user = [\n `现在写第 ${chapter.no} 章,标题《${chapter.title}》。`,\n `本章剧情要点:${chapter.beats}`,\n '',\n recentFacts.length > 0\n ? `本书已确立的事实(新写内容不得与之矛盾):\\n${recentFacts.join('\\n')}`\n : '',\n relatedFacts.length > 0\n ? `本章相关的既往事实(同样不得违背):\\n${relatedFacts.join('\\n')}`\n : '',\n prevSummary !== undefined && prevSummary !== ''\n ? `上一章摘要:${prevSummary}`\n : '',\n continuity !== ''\n ? `上一章结尾(用于衔接,不要复述):\\n${continuity}`\n : '这是第一章,注意开篇要有吸引力。',\n '',\n `请写 ${chapter.targetChars} 字左右的正文,只输出正文。`,\n ].filter(line => line !== '').join('\\n')\n\n const messages: Message[] = [createUserMessage({\n content: [{ type: 'text', text: user }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n })]\n const request: GenerateOptions = {\n provider: config.provider,\n model: config.model,\n messages,\n system: writeSystemPrompt(project),\n // Full-chapter output: budget generously (4000 chars ≈ 8-12k tokens,\n // plus the model's reasoning channel).\n maxTokens: Math.max(config.maxTokens, 20000),\n temperature: 0.85,\n }\n\n yield { frame: 'start' }\n\n const assembler = new BlockAssembler()\n let streamError: Error | undefined\n for await (const chunk of ctx.llm.stream(request)) {\n assembler.push(chunk)\n if (chunk.type === 'text-delta') {\n yield { frame: 'delta', text: chunk.text }\n }\n }\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n streamError = new Error(`生成失败(${finish.kind}): ${finish.failure.message}`)\n } else if (finish.kind === 'max-tokens') {\n streamError = new Error('达到 maxTokens 上限,正文可能不完整,请增大 maxTokens 后重试')\n }\n const body = assembler\n .blocks()\n .filter((block): block is Extract<StreamChunk, { type: 'block-end' }>['block'] & { type: 'text' } => block.type === 'text')\n .map(block => block.text)\n .join('\\n')\n .trim()\n if (streamError !== undefined) throw streamError\n if (body.length < 100) throw new Error('生成内容过短,可能失败,请重试')\n\n // Write the chapter file.\n const fileName = chapterFileName(chapter)\n mkdirSync(outputDir, { recursive: true })\n writeFileSync(join(outputDir, fileName), `# 第${chapter.no}章 ${chapter.title}\\n\\n${body}\\n`, 'utf8')\n\n chapter.status = 'written'\n chapter.chars = body.length\n chapter.file = fileName\n chapter.error = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n\n yield { frame: 'done', file: fileName, chars: body.length }\n}\n\n/** Generate a chapter summary (narrative memory). */\nexport async function summarizeChapter(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): Promise<string> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) throw new Error(`章节 ${chapterNo} 不在计划中`)\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) throw new Error(`章节 ${chapterNo} 的正文文件不存在`)\n const system = [\n '你是一位网文编辑。请为下面一章写一段 120-200 字的摘要,供后续章节写作时保持连贯性。',\n '摘要必须包含:本章发生的关键事件、主角状态变化(境界/资源/伤势/心境)、新增的伏笔或线索、角色关系变化。',\n '用客观陈述句,不要评价,不要剧透式感叹。只输出摘要正文。',\n ].join('\\n')\n const user = body.replace(/^#\\s+.*$/m, '').trim()\n const summary = await complete(ctx, config, { system, user, temperature: 0.3, maxTokens: Math.max(config.maxTokens, 4000) })\n chapter.summary = summary.slice(0, 500)\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return chapter.summary\n}\n\n/**\n * 摘要 + 事实抽取合并为一次 LLM 调用(省一次调用与一次正文输入,\n * 批量生成时整体开销约省 25%)。\n * @returns 摘要与新增事实条数(失败返回空,调用方 best-effort)。\n */\nexport async function summarizeAndExtractFacts(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): Promise<{ summary: string; factCount: number }> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) return { summary: '', factCount: 0 }\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) return { summary: '', factCount: 0 }\n const system = [\n '你是一位网文编辑。请为下面一章做两件事,输出合法 JSON 对象:',\n '{\"summary\": \"120-200字摘要,含关键事件/主角状态变化(境界资源伤势心境)/新增伏笔线索/角色关系变化,客观陈述不评价\", \"facts\": [\"已确立事实1\", \"…3-6条\"]}',\n 'facts 指:本章明确写出的、对后续有约束力的事实——人物当前状态、重要关系变化、地点与时间线、已落地或新增的伏笔线索、关键道具去向。',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n const user = body.replace(/^#\\s+.*$/m, '').trim()\n const text = await complete(ctx, config, { system, user, temperature: 0.2, maxTokens: Math.max(config.maxTokens, 5000) })\n const raw = parseJsonObject<{ summary?: unknown; facts?: unknown }>(text)\n const summary = typeof raw.summary === 'string' ? raw.summary.trim().slice(0, 500) : ''\n const factLines = Array.isArray(raw.facts)\n ? raw.facts\n .filter((v): v is string => typeof v === 'string' && v.trim().length > 8)\n .map(v => v.trim().slice(0, 140))\n : []\n if (summary !== '') chapter.summary = summary\n const list = project.facts ?? []\n for (const line of factLines.slice(0, 8)) list.push({ chapterNo, text: line })\n project.facts = list.slice(-300)\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return { summary, factCount: factLines.length }\n}\n\n/**\n * 抽取本章「已确立事实」追加到事实库/时间线(最多 300 条,最新优先)。\n * 事实注入后续章节生成提示词,保证人物状态/境界/资源/关系长期一致。\n * @returns 新增事实条数(失败返回 0,调用方 best-effort)。\n */\nexport async function extractFacts(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n chapterNo: number,\n): Promise<number> {\n const chapter = project.chapters.find(c => c.no === chapterNo)\n if (chapter === undefined) return 0\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) return 0\n const system = [\n '你是一位网文编辑。请从本章正文中抽取「已确立事实」,供后续章节保持一致。',\n '事实指:人物当前状态(境界/修为/伤势/资源/心境)、重要关系变化、地点与时间线、已落地或新增的伏笔线索、关键道具去向。',\n '要求:',\n '1. 只抽取本章明确写出的、对后续有约束力的内容;纯心理活动与无关细节不要。',\n '2. 每行一条事实,用客观陈述句,不含主观评价。',\n '3. 输出 3-6 条,每行一条,不要编号、不要前缀、不要解释。',\n ].join('\\n')\n const user = body.replace(/^#\\s+.*$/m, '').trim()\n // v4-flash 推理模型:reasoning channel 也占 maxTokens,预算给足避免截断。\n const text = await complete(ctx, config, { system, user, temperature: 0.2, maxTokens: Math.max(config.maxTokens, 4000) })\n const lines = text.split('\\n')\n .map(line => line.replace(/^[-*\\d.\\s]+/, '').trim())\n .filter(line => line.length > 8)\n .slice(0, 8)\n if (lines.length === 0) return 0\n const facts = project.facts ?? []\n for (const line of lines) facts.push({ chapterNo, text: line.slice(0, 140) })\n project.facts = facts.slice(-300)\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return lines.length\n}\n\n// ------------------------------------------------------------ book audit\n\nconst AUDIT_BATCH_SIZE = 10\n\n/** 单批质检:设定 + 事实库 + 该批章节节选 → 矛盾清单。 */\nasync function auditBatch(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n batch: ChapterPlan[],\n): Promise<AuditIssue[]> {\n const system = [\n '你是一位严谨的网文连续性审校编辑。你会收到一本小说的设定圣经、事实库和一批章节正文节选。',\n '请找出这批章节中的一致性矛盾,例如:',\n '- 人物状态冲突:境界/修为/伤势/资源在同一章内或跨章前后矛盾。',\n '- 设定违背:正文与世界观规则、金手指规则、写作红线冲突。',\n '- 时间线错乱:事件顺序、时间跨度、地点移动不合逻辑。',\n '- 细节穿帮:人名/地名/物品/数字前后不一致。',\n '要求:',\n '1. 只报告有实质证据的矛盾,不要泛泛而谈写作质量问题。',\n '2. 每条必须定位到具体章节号。',\n '3. 输出必须是合法 JSON 数组,格式:[{\"chapterNo\": 章节号, \"severity\": \"high|medium|low\", \"item\": \"矛盾描述\", \"suggestion\": \"修改建议\"}]',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n const factsBlock = (project.facts ?? []).slice(-60).map(f => `[第${f.chapterNo}章] ${f.text}`).join('\\n')\n const chapterBlocks = batch.map(c => {\n const body = readChapterFile(outputDir, c)\n const excerpt = (body ?? '').replace(/^#\\s+.*$/m, '').trim().slice(0, 700)\n return `【第${c.no}章《${c.title}》】\\n${excerpt}`\n }).join('\\n\\n')\n const user = [\n '请对以下小说做一致性质检。',\n project.bible !== undefined\n ? '设定圣经:\\n' + [\n project.bible.worldRules.length > 0 ? `世界规则:\\n${project.bible.worldRules.map(r => `- ${r}`).join('\\n')}` : '',\n project.bible.redLines.length > 0 ? `写作红线:\\n${project.bible.redLines.map(r => `- ${r}`).join('\\n')}` : '',\n project.bible.characters.length > 0 ? `角色:\\n${project.bible.characters.map(ch => `- ${ch.name}(${ch.traits.join('、')})`).join('\\n')}` : '',\n ].filter(s => s !== '').join('\\n')\n : '',\n factsBlock !== '' ? `已确立事实库:\\n${factsBlock}` : '',\n `正文节选(每章前 700 字):\\n${chapterBlocks}`,\n '只输出 JSON 数组。',\n ].filter(s => s !== '').join('\\n\\n')\n const text = await complete(ctx, config, { system, user, temperature: 0.2, maxTokens: Math.max(config.maxTokens, 12000) })\n const parsed = parseJsonArray<Record<string, unknown>>(text)\n const issues: AuditIssue[] = []\n for (const entry of parsed) {\n const item = typeof entry.item === 'string' ? entry.item : ''\n if (item === '') continue\n issues.push({\n chapterNo: Number(entry.chapterNo) || 0,\n severity: ['high', 'medium', 'low'].includes(entry.severity as string)\n ? entry.severity as AuditIssue['severity']\n : 'medium',\n item,\n suggestion: typeof entry.suggestion === 'string' ? entry.suggestion : '',\n })\n }\n return issues\n}\n\n/** 全书一致性质检:LLM 分批扫描已生成章节 + 设定 + 事实库,聚合矛盾清单。 */\nexport async function auditBook(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n): Promise<AuditIssue[]> {\n const written = project.chapters.filter(c => c.status !== 'pending' && c.status !== 'generating')\n if (written.length === 0) return []\n // 分批:每批 AUDIT_BATCH_SIZE 章,避免超长后单次爆上下文。\n const all: AuditIssue[] = []\n for (let i = 0; i < written.length; i += AUDIT_BATCH_SIZE) {\n const batch = written.slice(i, i + AUDIT_BATCH_SIZE)\n try {\n all.push(...await auditBatch(ctx, config, project, outputDir, batch))\n } catch { /* 单批失败不阻断其余批次 */ }\n }\n return all.slice(0, 50)\n}\n\n/** 小说简介:AI 生成或按已写开头补全(面向读者的作品门面)。 */\nexport async function generateBlurb(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n partial = '',\n): Promise<string> {\n const system = [\n '你是一位网文平台编辑,擅长写抓人的作品简介。',\n '要求:',\n '1. 120-250 字,突出核心卖点(金手指/题材/爽点/人设反差),用一两句抛出开局钩子。',\n '2. 不剧透结局与关键反转;语气贴合题材(热血/悬疑/轻松/虐心)。',\n '3. 中文,直接输出简介正文,不要 Markdown、不要引号包裹、不要「简介:」前缀。',\n ].join('\\n')\n const genreBlock = project.bible?.genre !== undefined ? `题材:${project.bible.genre}` : ''\n const volumeBlock = (project.volumes ?? []).slice(0, 3).map(v => v.title).join('、')\n const user = [\n `书名:《${project.bookName}》`,\n genreBlock,\n volumeBlock !== '' ? `卷结构:${volumeBlock}` : '',\n `已写章节数:${project.chapters.filter(c => c.status !== 'pending' && c.status !== 'generating').length}`,\n '大纲节选:\\n' + project.outline.slice(0, 2500),\n partial.trim() !== ''\n ? `已有开头草稿(请保留其内容与语气,续写补全为完整简介):\\n${partial.trim()}`\n : '请全量生成一份完整简介。',\n ].filter(s => s !== '').join('\\n\\n')\n const text = await complete(ctx, config, { system, user, temperature: 0.7, maxTokens: Math.max(config.maxTokens, 4000) })\n const blurb = text.replace(/^[\"'「『]|[\"'」』]$/g, '').replace(/^简介[::]\\s*/, '').trim().slice(0, 600)\n return blurb\n}\n\n// ---------------------------------------------------------------- world\n\n/**\n * 组装全书上下文包(AI 助手 book_overview 工具)。\n * 分片策略:章节要点默认只给最近 30 章(避免超长后爆上下文);\n * scope='full' 全量;scope=数字 只给该卷章节。\n */\nexport function bookOverview(project: ProjectState, scope: 'recent' | 'full' | number = 'recent'): string {\n const s: string[] = []\n s.push(`书名:${project.bookName}`)\n s.push(`【大纲全文】\\n${project.outline}`)\n if (project.bible !== undefined) {\n const bible = project.bible\n s.push('【设定圣经】')\n if (bible.genre !== '') s.push(`题材基调:${bible.genre}`)\n if (bible.worldRules.length > 0) s.push('世界规则:\\n' + bible.worldRules.map(r => `- ${r}`).join('\\n'))\n if (bible.characters.length > 0) {\n s.push('角色卡:')\n for (const card of bible.characters) {\n const roleName = { protagonist: '主角', supporting: '配角', antagonist: '反派', other: '其他' }[card.role]\n s.push(`- ${card.name}(${roleName}):${card.traits.join('、')}${card.goals !== '' ? `;目标:${card.goals}` : ''}${card.relations !== '' ? `;关系:${card.relations}` : ''}`)\n }\n }\n if (bible.redLines.length > 0) s.push('写作红线:\\n' + bible.redLines.map(r => `- ${r}`).join('\\n'))\n if (bible.style.length > 0) s.push('风格要求:\\n' + bible.style.map(r => `- ${r}`).join('\\n'))\n }\n const worldBlock = renderWorld(project.world)\n if (worldBlock !== '') s.push(worldBlock)\n if (project.volumes !== undefined && project.volumes.length > 0) {\n s.push('【卷结构】')\n for (const v of project.volumes) {\n s.push(`第${v.no}卷《${v.title}》:${v.summary}(章节 ${v.chapterStart}-${v.chapterEnd})`)\n }\n }\n if (project.chapters.length > 0) {\n // 分片:默认最近 30 章;full 全量;数字 = 指定卷。\n const maxNo = project.chapters.reduce((m, c) => Math.max(m, c.no), 0)\n const shown = project.chapters.filter(c => {\n if (scope === 'full') return true\n if (typeof scope === 'number') return c.volume === scope\n return c.no > Math.max(0, maxNo - 30)\n })\n const label = scope === 'full' ? '全部章节(标题/状态/剧情要点/摘要)' : typeof scope === 'number' ? `第 ${scope} 卷章节(标题/状态/剧情要点/摘要)` : `最近 ${shown.length} 章(标题/状态/剧情要点/摘要)`\n s.push(`【${label}】`)\n const statusText: Record<string, string> = { pending: '待生成', generating: '生成中', written: '待审稿', reviewing: '审稿中', approved: '已通过', rejected: '待修订', error: '失败' }\n for (const c of shown) {\n s.push(`第${c.no}章《${c.title}》[${statusText[c.status] ?? c.status}]${c.chars !== undefined ? ` ${c.chars}字` : ''}\\n剧情要点:${c.beats}\\n摘要:${c.summary ?? '无'}`)\n }\n if (scope !== 'full' && project.chapters.length > shown.length) {\n s.push(`(还有 ${project.chapters.length - shown.length} 章未列出,可用 scope=volume:N 查看指定卷)`)\n }\n }\n if ((project.facts ?? []).length > 0) {\n s.push('【事实库(最近 40 条;更多用 facts_query 检索)】')\n for (const f of (project.facts ?? []).slice(-40)) {\n s.push(`- [第${f.chapterNo}章] ${f.text}`)\n }\n }\n if (project.foreshadows.length > 0) {\n s.push('【伏笔】')\n for (const f of project.foreshadows) {\n s.push(`- [${f.status}] ${f.description}${f.targetChapter !== undefined ? `(预计 ${f.targetChapter} 章回收)` : ''}`)\n }\n }\n if (project.blurb !== undefined && project.blurb !== '') s.push(`【小说简介】${project.blurb}`)\n return s.join('\\n\\n')\n}\n\n/** 一条影响分析结果(改动波及处)。 */\nexport interface ImpactItem {\n /** 位置:章节号 / 大纲 / 设定圣经 / 大世界 / 事实库 / 简介。 */\n location: string\n /** 原文片段(定位用)。 */\n quote: string\n /** 修改建议。 */\n suggestion: string\n /** must = 必须同步改;optional = 建议改;note = 备注(如保留旧称作古称)。 */\n kind: 'must' | 'optional' | 'note'\n}\n\n/**\n * 影响分析:LLM 扫描全书(大纲/设定/大世界/事实库/已写章节),\n * 定位一次改动波及的所有位置。助手在修改后主动调用,做连锁维护。\n */\nexport async function analyzeImpact(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n change: string,\n): Promise<ImpactItem[]> {\n const system = [\n '你是一位网文一致性审校。作者要做一处修改,请找出这次改动会波及的所有位置(设定、大纲、已写章节正文、事实库、简介中可能因此过时或矛盾的内容)。',\n '输出必须是合法 JSON 数组,格式:[{\"location\": \"位置(第N章/大纲/设定圣经-世界规则/大世界-境界/事实库/简介)\", \"quote\": \"原文片段(20-60字)\", \"suggestion\": \"修改建议\", \"kind\": \"must|optional|note\"}]',\n 'kind 含义:must=必须同步改否则矛盾;optional=建议改(影响观感);note=备注(如旧称保留为古称、或无需改但需知晓)。',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n const written = project.chapters.filter(c => c.status !== 'pending' && c.status !== 'generating')\n // 轻量 base:大纲节选 + 道藏要点 + 编年录最近 40 条(替代全量 bookOverview,\n // 定位主要靠各批章节节选原文)。\n const base = [\n `要做的修改:${change}`,\n '以下为全书设定与规则要点(章节为分批节选):',\n `大纲节选:\\n${project.outline.slice(0, 2000)}`,\n project.bible !== undefined\n ? `道藏:${project.bible.worldRules.length} 条世界规则 / ${project.bible.redLines.length} 条红线 / 人物 ${project.bible.characters.map(c => c.name).join('、')}`\n : '',\n (project.facts ?? []).length > 0\n ? `编年录最近 40 条:\\n${(project.facts ?? []).slice(-40).map(f => `[第${f.chapterNo}章] ${f.text}`).join('\\n')}`\n : '',\n ].filter(s => s !== '').join('\\n\\n')\n const items: ImpactItem[] = []\n // 分批扫描章节正文(每批 8 章),聚合影响清单,避免超长后爆上下文。\n const IMPACT_BATCH_SIZE = 8\n for (let i = 0; i < written.length; i += IMPACT_BATCH_SIZE) {\n const batch = written.slice(i, i + IMPACT_BATCH_SIZE)\n const chapterBlock = batch.map(c => {\n const body = readChapterFile(outputDir, c)\n const excerpt = (body ?? '').replace(/^#\\s+.*$/m, '').trim().slice(0, 500)\n return `【第${c.no}章《${c.title}》】\\n${excerpt}`\n }).join('\\n\\n')\n const user = `${base}\\n\\n本批章节(第 ${batch[0]!.no}-${batch[batch.length - 1]!.no} 章):\\n${chapterBlock}\\n\\n只输出 JSON 数组。`\n try {\n const text = await complete(ctx, config, { system, user, temperature: 0.2, maxTokens: Math.max(config.maxTokens, 12000) })\n for (const entry of parseJsonArray<Record<string, unknown>>(text)) {\n const quote = typeof entry.quote === 'string' ? entry.quote.trim() : ''\n if (quote === '') continue\n items.push({\n location: typeof entry.location === 'string' ? entry.location : '未定位',\n quote: quote.slice(0, 120),\n suggestion: typeof entry.suggestion === 'string' ? entry.suggestion : '',\n kind: entry.kind === 'must' || entry.kind === 'optional' || entry.kind === 'note' ? entry.kind : 'optional',\n })\n }\n } catch { /* 单批失败不阻断其余批次 */ }\n }\n return items.slice(0, 30)\n}\n\n/** 把大世界结构化数据渲染成提示词块(境界体系按顺序强约束)。 */\nexport function renderWorld(world: WorldState | undefined): string {\n if (world === undefined) return ''\n const sections: string[] = ['==================== 大世界(结构化设定,写作时严格遵守) ====================']\n if (world.realms.length > 0) {\n sections.push('境界体系(由低到高,不得随意跳级或自创境界):')\n world.realms.forEach((realm, i) => {\n sections.push(`${i + 1}. ${realm.name}${realm.description !== '' ? ` — ${realm.description}` : ''}`)\n })\n }\n if (world.regions.length > 0) {\n sections.push('地理区域:')\n for (const region of world.regions) {\n sections.push(`- ${region.name}${region.description !== '' ? `:${region.description}` : ''}${region.faction !== undefined && region.faction !== '' ? `(势力:${region.faction})` : ''}`)\n }\n }\n if (world.factions.length > 0) {\n sections.push('势力分布:')\n for (const faction of world.factions) {\n sections.push(`- ${faction.name}(${faction.kind})${faction.description !== '' ? `:${faction.description}` : ''}${faction.region !== undefined && faction.region !== '' ? `(驻地:${faction.region})` : ''}`)\n }\n }\n return sections.join('\\n')\n}\n\n/** AI 提炼大世界:从大纲 + 设定圣经生成结构化境界体系/区域/势力。 */\nexport async function extractWorld(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n): Promise<WorldState> {\n const system = [\n '你是一位网文世界观架构师。请根据小说大纲与设定圣经,提炼结构化「大世界」数据。',\n '输出必须是合法 JSON 对象:',\n '{\"realms\": [{\"name\": \"境界名\", \"description\": \"突破条件/寿命/标志等\"}], \"regions\": [{\"name\": \"区域名\", \"description\": \"描述\", \"faction\": \"关联势力名或空\"}], \"factions\": [{\"name\": \"势力名\", \"kind\": \"宗门/家族/王朝/组织等\", \"description\": \"描述\", \"region\": \"驻地区域或空\"}]}',\n '要求:',\n '1. realms 按由低到高顺序排列(修仙题材必须含完整境界链;无境界设定的题材可输出空数组)。',\n '2. 数量贴合大纲:realms 3-12 个,regions 2-10 个,factions 2-10 个。',\n '3. 内容严格来自大纲与设定圣经,不要凭空发明与大纲冲突的设定。',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n const bibleBlock = project.bible !== undefined\n ? [\n project.bible.genre !== '' ? `题材:${project.bible.genre}` : '',\n project.bible.worldRules.length > 0 ? `世界规则:\\n${project.bible.worldRules.map(r => `- ${r}`).join('\\n')}` : '',\n ].filter(s => s !== '').join('\\n')\n : ''\n const user = [\n '请为这部小说提炼大世界数据。',\n `书名:《${project.bookName}》`,\n bibleBlock !== '' ? bibleBlock : '',\n '大纲:\\n' + project.outline.slice(0, 5000),\n '只输出 JSON 对象。',\n ].filter(s => s !== '').join('\\n\\n')\n const text = await complete(ctx, config, { system, user, temperature: 0.3, maxTokens: Math.max(config.maxTokens, 12000) })\n const raw = parseJsonObject<{ realms?: unknown; regions?: unknown; factions?: unknown }>(text)\n const str = (value: unknown): string => typeof value === 'string' ? value.trim() : ''\n const objArray = (value: unknown): Record<string, unknown>[] =>\n Array.isArray(value) ? value.filter((v): v is Record<string, unknown> => typeof v === 'object' && v !== null) : []\n const world: WorldState = {\n realms: objArray(raw.realms).map(entry => ({\n name: str(entry.name).slice(0, 20) || '未命名境界',\n description: str(entry.description).slice(0, 200),\n })).filter(r => r.name !== '未命名境界' || r.description !== ''),\n regions: objArray(raw.regions).map(entry => ({\n name: str(entry.name).slice(0, 30) || '未命名区域',\n description: str(entry.description).slice(0, 200),\n faction: str(entry.faction).slice(0, 30),\n })).filter(r => r.name !== '未命名区域' || r.description !== ''),\n factions: objArray(raw.factions).map(entry => ({\n name: str(entry.name).slice(0, 30) || '未命名势力',\n kind: str(entry.kind).slice(0, 20) || '组织',\n description: str(entry.description).slice(0, 200),\n region: str(entry.region).slice(0, 30),\n })).filter(f => f.name !== '未命名势力' || f.description !== ''),\n }\n return world\n}\n\n/**\n * 事实库回填:对历史已生成章节批量抽取事实(无事实记录的旧章节)。\n * @returns 回填的章节数。\n */\nexport async function backfillFacts(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n): Promise<number> {\n const have = new Set((project.facts ?? []).map(f => f.chapterNo))\n let filled = 0\n for (const chapter of project.chapters) {\n if (chapter.status === 'pending' || chapter.status === 'generating') continue\n if (chapter.file === undefined || have.has(chapter.no)) continue\n try {\n const n = await extractFacts(ctx, config, project, outputDir, chapter.no)\n if (n > 0) filled++\n } catch { /* best-effort per chapter */ }\n have.add(chapter.no)\n }\n return filled\n}\n\n/**\n * 角色卡刷新:出场统计由服务端从正文精确计算(角色名出现过的章节数、\n * 最近出现章节),LLM 只负责聚合「当前状态」一句话。\n */\nexport async function refreshCharacters(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n): Promise<RoleStatusCard[]> {\n const roster = project.bible?.characters ?? []\n const facts = project.facts ?? []\n if (roster.length === 0 && facts.length === 0) return []\n\n // 服务端精确出场统计:遍历已写章节正文,统计每个角色名出现过的章节。\n const stat = new Map<string, { chapters: Set<number>; last: number }>()\n const known = roster.map(card => card.name)\n for (const chapter of project.chapters) {\n if (chapter.status === 'pending' || chapter.status === 'generating') continue\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) continue\n for (const name of known) {\n if (body.includes(name)) {\n const entry = stat.get(name) ?? { chapters: new Set<number>(), last: 0 }\n entry.chapters.add(chapter.no)\n if (chapter.no > entry.last) entry.last = chapter.no\n stat.set(name, entry)\n }\n }\n }\n\n // LLM 只聚合状态:名单(含 traits)+ 事实库 → [{name, status}]\n let statuses = new Map<string, string>()\n if (facts.length > 0) {\n const system = [\n '你是一位网文角色档案管理员。请根据「角色名单」与「已确立事实库」,为每个角色输出「当前状态」一句话(境界/修为/伤势/资源/心境)。',\n '输出必须是合法 JSON 数组,格式:[{\"name\": \"角色名\", \"status\": \"当前状态一句话\"}]',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n const rosterBlock = roster.map(ch => `- ${ch.name}(${ch.traits.join('、')})`).join('\\n')\n const factsBlock = facts.map(f => `[第${f.chapterNo}章] ${f.text}`).join('\\n')\n const user = [\n `角色名单:\\n${rosterBlock}`,\n `已确立事实库(${facts.length} 条):\\n${factsBlock.slice(-6000)}`,\n '只输出 JSON 数组。',\n ].join('\\n\\n')\n try {\n const text = await complete(ctx, config, { system, user, temperature: 0.2, maxTokens: Math.max(config.maxTokens, 8000) })\n for (const entry of parseJsonArray<Record<string, unknown>>(text)) {\n const name = typeof entry.name === 'string' ? entry.name : ''\n if (name !== '' && typeof entry.status === 'string') statuses.set(name, entry.status)\n }\n } catch { /* status 聚合失败则只给出场统计 */ }\n }\n\n // 合并:出场统计(精确)+ 状态(LLM)+ 名单角色补全。\n const cards: RoleStatusCard[] = []\n const roleOf = (name: string): string => roster.find(c => c.name === name)?.role ?? 'other'\n for (const card of roster) {\n const entry = stat.get(card.name)\n cards.push({\n name: card.name,\n role: card.role,\n status: statuses.get(card.name) ?? '',\n lastChapter: entry?.last ?? 0,\n appearances: entry?.chapters.size ?? 0,\n })\n }\n // 名单外的角色(从事实库中识别到但不在设定圣经名单)仅当有出场统计时补充。\n for (const [name, entry] of stat) {\n if (!cards.some(c => c.name === name)) {\n cards.push({\n name,\n role: roleOf(name),\n status: statuses.get(name) ?? '',\n lastChapter: entry.last,\n appearances: entry.chapters.size,\n })\n }\n }\n return cards\n}\n\n// ------------------------------------------------------------- foreshadows\n\n/** System prompt for foreshadow suggestions. */\nfunction foreshadowSystemPrompt(): string {\n return [\n '你是一位网文伏笔设计师。你会收到大纲和已写的章节信息,请为小说建议 3-8 条值得埋设的伏笔。',\n '要求:',\n '1. 伏笔必须有明确的回收价值(推动主线、人物弧光、世界观揭秘)。',\n '2. 描述要具体,指出埋设章节与预计回收章节(可空缺)。',\n '3. 优先从大纲的暗线(如记忆代价、残片收集、身世谜团)中提炼。',\n '输出必须是合法 JSON 数组:',\n '[{\"description\": \"伏笔描述\", \"plantedChapter\": 章节号或null, \"targetChapter\": 章节号或null}]',\n '重要:所有字符串值内部不得包含换行符,JSON 必须在一段内完整结束。',\n ].join('\\n')\n}\n\n/** Suggest foreshadows from the outline + plan. */\nexport async function suggestForeshadows(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n): Promise<Foreshadow[]> {\n const user = [\n '请为下面这部小说设计伏笔。',\n `大纲:\\n${project.outline}`,\n `已规划章节数:${project.chapters.length}`,\n ].join('\\n')\n const text = await complete(ctx, config, { system: foreshadowSystemPrompt(), user, temperature: 0.5, maxTokens: Math.max(config.maxTokens, 12000) })\n const parsed = parseJsonArray<Record<string, unknown>>(text)\n const existing = new Set(project.foreshadows.map(f => f.description))\n const created: Foreshadow[] = []\n for (const entry of parsed) {\n if (typeof entry !== 'object' || entry === null) continue\n const description = typeof entry.description === 'string' ? entry.description.trim() : ''\n if (description === '' || existing.has(description)) continue\n existing.add(description)\n created.push({\n id: `fs-${Date.now().toString(36)}-${created.length}`,\n description: description.slice(0, 200),\n plantedChapter: typeof entry.plantedChapter === 'number' ? entry.plantedChapter : undefined,\n targetChapter: typeof entry.targetChapter === 'number' ? entry.targetChapter : undefined,\n status: 'planned',\n })\n }\n project.foreshadows.push(...created)\n project.updatedAt = new Date().toISOString()\n return created\n}\n\n// -------------------------------------------------------------- style asset\n\n/**\n * 写法引擎:从样本文本提取一份写法资产(叙事风格规则)。\n * @returns 提取出的风格规则(未持久化,由调用方存入 project.assets)。\n */\nexport async function extractStyleAsset(\n ctx: Context,\n config: NovelConfig,\n sampleText: string,\n): Promise<{ proseRules: string[]; dialogueRules: string[]; descriptionRules: string[]; boundaries: string[] }> {\n const user = `请分析下面这段样本文本,提炼其叙事风格规则:\\n\\n${sampleText}`\n const text = await complete(ctx, config, { system: styleEngineSystemPrompt(), user, temperature: 0.3, maxTokens: Math.max(config.maxTokens, 12000) })\n const raw = parseJsonObject<{ proseRules?: unknown; dialogueRules?: unknown; descriptionRules?: unknown; boundaries?: unknown }>(text)\n const strArray = (value: unknown): string[] =>\n Array.isArray(value) ? value.filter((v): v is string => typeof v === 'string' && v.trim() !== '') : []\n const result = {\n proseRules: strArray(raw.proseRules),\n dialogueRules: strArray(raw.dialogueRules),\n descriptionRules: strArray(raw.descriptionRules),\n boundaries: strArray(raw.boundaries),\n }\n if (result.proseRules.length + result.dialogueRules.length + result.descriptionRules.length + result.boundaries.length === 0) {\n throw new Error('写法提取失败:模型没有返回有效规则')\n }\n return result\n}\n\n// ------------------------------------------------------------------ export\n\n/** Export the whole book as one txt/md file. */\nexport function exportBook(outputDir: string, project: ProjectState, format: 'txt' | 'md'): { file: string; chars: number; chapters: number } {\n const parts: string[] = []\n if (format === 'md') {\n parts.push(`# ${project.bookName}\\n`)\n } else {\n parts.push(project.bookName, '')\n }\n const done = project.chapters.filter(c => c.file !== undefined)\n for (const chapter of done) {\n const body = readChapterFile(outputDir, chapter) ?? ''\n if (format === 'md') {\n parts.push(`\\n## 第${chapter.no}章 ${chapter.title}\\n`, body.trim(), '')\n } else {\n parts.push('', `第${chapter.no}章 ${chapter.title}`, '', body.trim(), '')\n }\n }\n const content = parts.join('\\n')\n const ext = format === 'md' ? 'md' : 'txt'\n const file = `《${safeFileName(project.bookName)}》全本.${ext}`\n writeFileSync(join(outputDir, file), content, 'utf8')\n return { file, chars: content.length, chapters: done.length }\n}\n","/**\n * AI assistant engine — a conversational editor over the novel project.\n *\n * The user talks to the assistant about plot, characters, settings; the\n * assistant can reply in prose AND emit action directives that the host\n * executes (rewrite a paragraph, edit the bible, regenerate a chapter,\n * export the book, ...). Conversation history persists next to the project\n * as NDJSON, so a reload keeps the thread.\n *\n * Action protocol: the model emits a line of the form\n * <dsh-action name=\"toolName\">{jsonArgs}</dsh-action>\n * anywhere in its reply. The host strips it, executes the tool, appends the\n * result as a tool-role message, and continues the loop (bounded rounds).\n */\n\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, appendFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createUserMessage, createAssistantMessage, BlockAssembler, type GenerateOptions, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm'\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { AssistantMessage, NovelConfig, ProjectState } from './protocol.ts'\nimport { emptyProjectAssets } from './assets.ts'\nimport {\n analyzeImpact,\n bookOverview,\n chapterFileName,\n exportBook,\n generateChapterStream,\n readChapterFile,\n reviewChapter,\n saveProject,\n summarizeAndExtractFacts,\n} from './engine.ts'\nimport { rewriteChapterStream } from './engine.ts'\n\n/** History file name inside the output dir. */\nexport const ASSISTANT_HISTORY_FILE = 'novel-assistant.jsonl'\n\n/** Max tool-call rounds per user turn (safety bound). */\nconst MAX_TOOL_ROUNDS = 6\n\n/** Max history messages kept in context (older ones summarized away). */\nconst MAX_HISTORY_MESSAGES = 24\n\n// ------------------------------------------------------------------ history\n\n/** Load the persisted conversation (empty when none). */\nexport function loadAssistantHistory(outputDir: string): AssistantMessage[] {\n const file = join(outputDir, ASSISTANT_HISTORY_FILE)\n if (!existsSync(file)) return []\n const messages: AssistantMessage[] = []\n try {\n for (const line of readFileSync(file, 'utf8').split('\\n')) {\n if (line.trim() === '') continue\n try {\n const parsed = JSON.parse(line) as AssistantMessage\n if (typeof parsed.role === 'string' && typeof parsed.content === 'string') messages.push(parsed)\n } catch { /* skip malformed line */ }\n }\n } catch { /* unreadable history -> start fresh */ }\n return messages\n}\n\n/** Append one message to the persisted history. Tool payloads (e.g. full\n * outline / chapter text) are capped so the jsonl and later LLM context\n * don't grow unboundedly. */\nfunction appendHistory(outputDir: string, message: AssistantMessage): void {\n mkdirSync(outputDir, { recursive: true })\n const entry: AssistantMessage = message.role === 'tool' && message.content.length > 4000\n ? { ...message, content: message.content.slice(0, 4000) + '\\n…(已截断,如需完整内容请重新调用工具)' }\n : message\n appendFileSync(join(outputDir, ASSISTANT_HISTORY_FILE), JSON.stringify(entry) + '\\n', 'utf8')\n}\n\n/** 清空助手对话记录(删除历史文件)。 */\nexport function clearAssistantHistory(outputDir: string): void {\n const file = join(outputDir, ASSISTANT_HISTORY_FILE)\n if (existsSync(file)) rmSync(file, { force: true })\n}\n\n// ----------------------------------------------------------------- context\n\n/** Render the project snapshot the assistant sees. */\nfunction renderProjectSnapshot(project: ProjectState): string {\n const sections: string[] = []\n sections.push(`书名:${project.bookName}`)\n sections.push(`大纲节选(如需全文用 outline_text 工具):\\n${project.outline.slice(0, 2500)}`)\n // 写作资产名称化(占位小,需要详情用 assets_status)。\n const assetNames: string[] = []\n if (project.assets?.genre !== undefined) assetNames.push(`题材:${project.assets.genre.name}`)\n if (project.assets?.primaryProgression !== undefined) assetNames.push(`主推进:${project.assets.primaryProgression.name}`)\n if ((project.assets?.styleAssets?.length ?? 0) > 0) assetNames.push(`写法:${project.assets!.styleAssets!.map(s => s.name).join('、')}`)\n if ((project.assets?.antiAiRules?.length ?? 0) > 0) assetNames.push(`文戒自定义:${project.assets!.antiAiRules!.map(r => r.name).join('、')}`)\n if (assetNames.length > 0) sections.push(`【写作资产】${assetNames.join(' · ')}`)\n if (project.bible !== undefined) {\n const bible = project.bible\n sections.push('【设定圣经】')\n if (bible.genre !== '') sections.push(`题材基调:${bible.genre}`)\n if (bible.worldRules.length > 0) sections.push('世界规则:\\n' + bible.worldRules.map(r => `- ${r}`).join('\\n'))\n if (bible.characters.length > 0) {\n sections.push('角色卡:')\n for (const card of bible.characters) {\n const roleName = { protagonist: '主角', supporting: '配角', antagonist: '反派', other: '其他' }[card.role]\n sections.push(`- ${card.name}(${roleName}):${card.traits.join('、')}${card.goals !== '' ? `;目标:${card.goals}` : ''}`)\n }\n }\n if (bible.redLines.length > 0) sections.push('写作红线:\\n' + bible.redLines.map(r => `- ${r}`).join('\\n'))\n }\n if (project.world !== undefined) {\n const world = project.world\n sections.push('【大世界】')\n if (world.realms.length > 0) sections.push('境界体系:' + world.realms.map((r, i) => `${i + 1}.${r.name}(${r.description.slice(0, 40)})`).join(' → '))\n if (world.regions.length > 0) sections.push('区域:' + world.regions.map(r => r.name).join('、'))\n if (world.factions.length > 0) sections.push('势力:' + world.factions.map(f => `${f.name}(${f.kind})`).join('、'))\n }\n if (project.volumes !== undefined && project.volumes.length > 0) {\n sections.push('【卷结构】')\n for (const v of project.volumes) {\n sections.push(`第${v.no}卷《${v.title}》:${v.summary}(章节 ${v.chapterStart}-${v.chapterEnd})`)\n }\n }\n if (project.chapters.length > 0) {\n // 快照只列最近 30 章(长书时防止上下文膨胀;更早章节可用\n // book_overview scope=volume:N 或 chapter_text 按需查看)。\n const shown = project.chapters.slice(-30)\n sections.push(`【章节计划与进度(最近 ${shown.length} 章)】`)\n for (const c of shown) {\n const statusText = { pending: '待生成', generating: '生成中', written: '待审稿', reviewing: '审稿中', approved: '已通过', rejected: '待修订', error: '失败' }[c.status]\n sections.push(`第${c.no}章《${c.title}》[${statusText}]${c.chars !== undefined ? ` ${c.chars}字` : ''}${c.summary !== undefined && c.summary !== '' ? ` 摘要:${c.summary}` : ''}`)\n }\n if (project.chapters.length > shown.length) {\n sections.push(`(还有 ${project.chapters.length - shown.length} 章未列出,可用 book_overview scope=volume:N 查看)`)\n }\n // 最近两章正文节选(讨论细节时参考)\n const written = project.chapters.filter(c => c.status === 'approved' || c.status === 'written')\n const recent = written.slice(-2)\n if (recent.length > 0) {\n sections.push('【最近章节正文节选】')\n for (const c of recent) {\n sections.push(`第${c.no}章《${c.title}》(节选,如需全文用 chapter_text):${c.beats.slice(0, 300)}`)\n }\n }\n }\n if (project.foreshadows.length > 0) {\n sections.push('【伏笔】')\n for (const f of project.foreshadows) {\n sections.push(`- [${f.status}] ${f.description}${f.targetChapter !== undefined ? `(预计 ${f.targetChapter} 章回收)` : ''}`)\n }\n }\n if ((project.facts ?? []).length > 0) {\n sections.push('【已确立事实库(最近 40 条,回答设定问题必须遵守)】')\n for (const f of (project.facts ?? []).slice(-40)) {\n sections.push(`- [第${f.chapterNo}章] ${f.text}`)\n }\n }\n if (project.blurb !== undefined && project.blurb !== '') {\n sections.push(`【小说简介】${project.blurb}`)\n }\n return sections.join('\\n')\n}\n\n/** The assistant system prompt. */\nfunction assistantSystemPrompt(project: ProjectState): string {\n return [\n '你是「编辑老师」——服务这本书作者的资深中文网文编辑。',\n '人设:二十年网文老编辑,懂套路、懂市场、懂节奏,说话直接但句句有用。',\n '座右铭:「书是你的,但坑我替你盯着。」',\n '职责边界:陪作者讨论剧情/人设/世界观/爽点节奏并落地修改、维护全书一致性;不闲聊、不彩虹屁、不无意义长篇大论。',\n '==================== 当前项目快照 ====================',\n renderProjectSnapshot(project),\n '==================== 快照结束 ====================',\n '',\n '工作规则(严格遵守):',\n '1. 全量知情:回答和修改必须基于项目真实数据,禁止编造书中不存在的设定。需要完整信息时,先调用 book_overview 获取全书上下文(大纲全文/设定圣经/大世界/事实库/全部章节要点/伏笔/简介);需要某章正文用 chapter_text。',\n '2. 修改流程:改前用一句话说明意图 → 执行工具 → 改后简要汇报。',\n '3. 连锁维护:改动可能波及其它位置(其它章节、设定、事实库、简介)时,执行后主动调用 impact_analysis 分析影响面,并把「必须同步」的项一并处理或明确提示作者逐项确认。',\n '4. 删除红线:删除章节、清空设定等破坏性操作必须等作者明确同意。',\n '5. 品质门槛:建议必须具体——指出问题在哪一章、哪一段、哪一句,并给出可落地的改法;禁止\"建议增强冲突\"这类空话。',\n '6. 设定忠诚:忠于大纲、设定圣经、大世界、事实库;发现书中已有内容与设定冲突时,主动指出并给修正方案。',\n '7. 中文回复,简洁有干货。',\n '',\n '可用工具:',\n '- book_overview:{\"scope\": \"recent|full|volume:2\"(可选,默认 recent)}。返回全书上下文包(大纲/道藏/大世界/章节要点/事实库/伏笔/简介)。recent=最近30章;full=全部章节(书很长时慎用);volume:N=只看第N卷。',\n '- facts_query:{\"keyword\": \"关键词\"}。从编年录(事实库)按关键词检索相关事实(如灵石、境界名、人物名)。',\n '- impact_analysis:{\"change\": \"要做的修改描述\"}。分析这次改动会波及哪些位置,返回影响清单(定位到章节/设定/事实库)。',\n '- outline_text:无参数。返回当前大纲全文。',\n '- outline_replace:{\"old\": \"要替换的原文片段\", \"new\": \"新文本\"}。在大纲中替换一段文字(old 必须能在大纲中找到)。',\n '- bible_set_rule:{\"index\": 序号(0起), \"text\": \"新规则文本\"} 或 {\"append\": \"追加的规则\"}。修改设定圣经的世界规则。',\n '- bible_set_redline:同上,修改写作红线。',\n '- chapter_text:{\"no\": 章节号}。返回该章正文。',\n '- chapter_rewrite:{\"no\": 章节号, \"instructions\": \"修改要求\", \"target\": \"原文片段(可选,留空整章)\"}。按讨论结果修订章节;给了 target 只改该自然段。',\n '- chapter_generate:{\"no\": 章节号}。重新生成该章。',\n '- chapter_review:{\"no\": 章节号}。对该章执行 AI 审稿。',\n '- foreshadow_add:{\"description\": \"伏笔描述\", \"targetChapter\": 预计回收章(可选)}。新增伏笔。',\n '- foreshadow_update:{\"id\": \"伏笔id\", \"status\": \"planned|planted|progressing|resolved|abandoned\"}。更新伏笔状态。',\n '- export_txt:无参数。导出全本 TXT。',\n '- assets_status:无参数。查看本书当前写作资产(题材/推进模式/反AI规则/写法)。',\n '- assets_set_genre:{\"name\": \"题材名\", \"description\": \"题材说明(可选)\"}。设置本书题材基底。',\n '- assets_set_progression:{\"name\": \"模式名\", \"driver\": \"驱动力\", \"primary\": true/false}。设置主/辅助推进模式。',\n '- assets_add_rule:{\"name\": \"规则名(可选)\", \"avoid\": \"要避免的表达问题\", \"fix\": \"修正方向(可选)}。新增反 AI 规则。',\n '',\n '回答质量要求(非常重要):',\n '- 具体:回答必须引用项目里的真实内容(人名、境界、章节、伏笔、设定),禁止空泛套话。快照里没有的信息,先调用工具获取(chapter_text / outline_text)再回答。',\n '- 专业:给建议时说明理由,指出问题所在章节/段落,给出可直接落地的修改方案(改什么、怎么改)。',\n '- 主动:作者说\"改一下\",主动调用对应工具执行,不要只给建议不动手;执行前用一句话说明意图,执行后简短汇报结果。',\n '- 忠于设定:以大纲、设定圣经、事实库为准,不得自相矛盾;发现问题(如剧情与设定冲突)主动指出。',\n '- 中文回复;文字量适中,别啰嗦。',\n '',\n '使用规则(非常重要):',\n '- 当你想执行任何工具时,你的【整个回复】必须只包含动作指令标签,格式如下(不要有任何解释文字、不要用自然语言说\"我要去改\",直接输出标签):',\n ' 正确示例:<dsh-action name=\"outline_replace\">{\"old\":\"要替换的原文\",\"new\":\"新文本\"}</dsh-action>',\n ' 正确示例:<dsh-action name=\"chapter_text\">{\"no\":1}</dsh-action>',\n ' 错误示例(绝对不要这样回复):\"好的,我先看一下大纲,马上改。\" ← 这只是文字,不会执行任何操作',\n '- 工具调用是自动的:你输出标签后,宿主会执行并把结果反馈给你,你再基于结果继续。',\n '- 每次回复最多调用 1 个动作;执行结果会反馈给你,你可以继续讨论或再调用。',\n '- 需要先看大纲/章节再决定怎么改?那就先输出一个 outline_text / chapter_text 的标签,等结果回来。',\n '- chapter_rewrite 的 target 参数:从章节正文中复制一小段(一句话或几句话即可),不要带换行、不要带引号,取连续文本片段。',\n '- 如果工具执行失败(例如片段未找到),根据错误信息修正参数后自动重试一次,不要直接放弃或让作者手动操作。',\n '- 修改前先向作者说明你要改什么、为什么;动作执行后简要汇报结果。',\n '- 涉及删除类操作(删除章节、清空设定)必须等作者明确同意。',\n '- 严格忠于设定圣经与大纲;不得自行发明与既有设定冲突的内容。',\n '- 用中文回复。',\n ].join('\\n')\n}\n\n// ------------------------------------------------------------- action exec\n\n/** Execute one action directive. Returns a text result (or throws). */\n/**\n * Execute one action directive as an async generator: yields live progress\n * text (chapter text being generated/rewritten), then yields the final result\n * string. Throws on failure.\n */\nexport async function* executeAction(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n name: string,\n args: Record<string, unknown>,\n): AsyncGenerator<string, string, unknown> {\n const str = (value: unknown): string => typeof value === 'string' ? value : ''\n const num = (value: unknown): number | undefined => typeof value === 'number' ? value : undefined\n\n /** Forward live text deltas from a streaming chapter job (text only). */\n const forward = async function* (stream: AsyncGenerator<{ frame: 'start' } | { frame: 'delta'; text: string } | { frame: 'done'; file: string; chars: number } | { frame: 'drafted'; chars: number; draft: string }, void, unknown>): AsyncGenerator<string, void, unknown> {\n for await (const step of stream) {\n if (step.frame === 'delta') yield step.text\n }\n }\n\n switch (name) {\n case 'book_overview': {\n // 全书上下文包(分片:默认最近 30 章;scope=full 全量;scope=volume:N 指定卷)。\n const scopeArg = str(args.scope)\n const scope = scopeArg === 'full'\n ? 'full' as const\n : /^volume:(\\d+)$/.test(scopeArg)\n ? Number(scopeArg.slice(7))\n : 'recent' as const\n return bookOverview(project, scope)\n }\n case 'facts_query': {\n // 从编年录(事实库)按关键词检索相关事实。\n const keyword = str(args.keyword).trim()\n if (keyword === '') throw new Error('facts_query 需要 keyword')\n const hits = (project.facts ?? []).filter(f => f.text.includes(keyword)).slice(-30)\n if (hits.length === 0) return `编年录中未找到与「${keyword}」相关的事实记录。`\n return `编年录中与「${keyword}」相关的事实(${hits.length} 条):\\n` + hits.map(f => `- [第${f.chapterNo}章] ${f.text}`).join('\\n')\n }\n case 'impact_analysis': {\n const change = str(args.change)\n if (change === '') throw new Error('impact_analysis 需要 change(要做的修改描述)')\n const items = await analyzeImpact(ctx, config, project, outputDir, change)\n if (items.length === 0) return '影响分析:未发现需要同步修改的位置。'\n const lines = items.map((it, i) => `${i + 1}. [${it.location}]「${it.quote}」${it.suggestion !== '' ? ` → ${it.suggestion}` : ''}(${it.kind === 'must' ? '必须同步' : it.kind === 'optional' ? '建议' : '备注'})`)\n return `影响分析:这次改动波及 ${items.length} 处——\\n${lines.join('\\n')}\\n请据此提示作者逐项处理;章节内的修改可引导作者在工作区查看。`\n }\n case 'outline_text': {\n return project.outline\n }\n case 'outline_replace': {\n const old = str(args.old)\n const next = str(args.new)\n if (old === '' || !project.outline.includes(old)) {\n throw new Error(`大纲中未找到片段「${old.slice(0, 40)}…」`)\n }\n project.outline = project.outline.replace(old, next)\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `大纲已修改:替换了 ${old.length} 字符的片段。`\n }\n case 'bible_set_rule': {\n if (project.bible === undefined) throw new Error('尚无道藏,请先提炼')\n const index = num(args.index)\n if (index !== undefined) {\n project.bible.worldRules[index] = str(args.text)\n } else if (str(args.append) !== '') {\n project.bible.worldRules.push(str(args.append))\n } else {\n throw new Error('bible_set_rule 需要 index+text 或 append')\n }\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `世界规则已更新(当前 ${project.bible.worldRules.length} 条)。`\n }\n case 'bible_set_redline': {\n if (project.bible === undefined) throw new Error('尚无道藏,请先提炼')\n const index = num(args.index)\n if (index !== undefined) {\n project.bible.redLines[index] = str(args.text)\n } else if (str(args.append) !== '') {\n project.bible.redLines.push(str(args.append))\n } else {\n throw new Error('bible_set_redline 需要 index+text 或 append')\n }\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `写作红线已更新(当前 ${project.bible.redLines.length} 条)。`\n }\n case 'chapter_text': {\n const no = num(args.no)\n if (no === undefined) throw new Error('chapter_text 需要 no')\n const chapter = project.chapters.find(c => c.no === no)\n if (chapter === undefined) throw new Error(`章节 ${no} 不存在`)\n const body = readChapterFile(outputDir, chapter)\n if (body === undefined) throw new Error(`章节 ${no} 尚未生成`)\n return body\n }\n case 'chapter_rewrite': {\n const no = num(args.no)\n if (no === undefined) throw new Error('chapter_rewrite 需要 no')\n const instructions = str(args.instructions)\n const target = str(args.target)\n for await (const chunk of forward(rewriteChapterStream(ctx, config, project, outputDir, no, instructions, target === '' ? undefined : target))) {\n yield chunk\n }\n // Draft mode: the assistant acts on the user's explicit instruction, so\n // apply the draft immediately — summarize/review must see the new body.\n const chapter = project.chapters.find(c => c.no === no)\n const draft = chapter?.pendingDraft\n if (chapter === undefined || draft === undefined || draft === '') {\n throw new Error(`章节 ${no} 修订后没有产出草稿`)\n }\n const fileName = chapterFileName(chapter)\n mkdirSync(outputDir, { recursive: true })\n writeFileSync(join(outputDir, fileName), `# 第${chapter.no}章 ${chapter.title}\\n\\n${draft}\\n`, 'utf8')\n chapter.pendingDraft = undefined\n chapter.status = 'written'\n chapter.chars = draft.length\n chapter.file = fileName\n chapter.review = undefined\n chapter.error = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n yield '(已采纳修订稿,正在生成章节摘要与编年录…)'\n try {\n await summarizeAndExtractFacts(ctx, config, project, outputDir, no)\n } catch { /* best-effort */ }\n yield '(正在 AI 审稿…)'\n const report = await reviewChapter(ctx, config, project, outputDir, no)\n return `章节 ${no} 已${target === '' ? '整章' : '局部'}修订完成(${project.chapters.find(c => c.no === no)?.chars ?? '?'} 字)。重新审稿:${report.score} 分 — ${report.verdict}`\n }\n case 'chapter_generate': {\n const no = num(args.no)\n if (no === undefined) throw new Error('chapter_generate 需要 no')\n for await (const chunk of forward(generateChapterStream(ctx, config, project, outputDir, no))) {\n yield chunk\n }\n yield '(正在生成章节摘要与编年录…)'\n try {\n await summarizeAndExtractFacts(ctx, config, project, outputDir, no)\n } catch { /* best-effort */ }\n yield '(正在 AI 审稿…)'\n const report = await reviewChapter(ctx, config, project, outputDir, no)\n return `章节 ${no} 已生成(${project.chapters.find(c => c.no === no)?.chars ?? '?'} 字)。审稿:${report.score} 分 — ${report.verdict}`\n }\n case 'chapter_review': {\n const no = num(args.no)\n if (no === undefined) throw new Error('chapter_review 需要 no')\n const report = await reviewChapter(ctx, config, project, outputDir, no)\n const issues = report.issues.map(i => `[${i.severity}] ${i.item} → ${i.suggestion}`).join('\\n')\n return `章节 ${no} 审稿:${report.score} 分 — ${report.verdict}\\n${issues}`\n }\n case 'foreshadow_add': {\n const description = str(args.description)\n if (description === '') throw new Error('foreshadow_add 需要 description')\n const targetChapter = num(args.targetChapter)\n project.foreshadows.push({\n id: `fs-${Date.now().toString(36)}`,\n description,\n targetChapter,\n status: 'planned',\n })\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `已新增伏笔:「${description.slice(0, 50)}」`\n }\n case 'foreshadow_update': {\n const id = str(args.id)\n const status = str(args.status) as 'planned' | 'planted' | 'progressing' | 'resolved' | 'abandoned'\n const target = project.foreshadows.find(f => f.id === id)\n if (target === undefined) throw new Error(`伏笔 ${id} 不存在`)\n if (!['planned', 'planted', 'progressing', 'resolved', 'abandoned'].includes(status)) {\n throw new Error(`非法状态 ${status}`)\n }\n target.status = status\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `伏笔已更新为 ${status}:「${target.description.slice(0, 50)}」`\n }\n case 'export_txt': {\n const result = exportBook(outputDir, project, 'txt')\n return `已导出 TXT:${result.file}(${result.chars} 字,${result.chapters} 章)`\n }\n case 'assets_status': {\n const assets = project.assets\n if (assets === undefined) return '本书尚未配置写作资产。'\n const parts: string[] = []\n if (assets.genre !== undefined) parts.push(`题材:${assets.genre.name}`)\n if (assets.primaryProgression !== undefined) parts.push(`主推进:${assets.primaryProgression.name}`)\n if (assets.auxiliaryProgressions.length > 0) parts.push(`辅助推进:${assets.auxiliaryProgressions.map(m => m.name).join('、')}`)\n if (assets.antiAiRules.length > 0) parts.push(`自定义反AI规则:${assets.antiAiRules.map(r => r.name).join('、')}`)\n if (assets.styleAssets.length > 0) parts.push(`写法资产:${assets.styleAssets.map(s => s.name).join('、')}`)\n return parts.length > 0 ? parts.join('\\n') : '本书尚未配置写作资产。'\n }\n case 'assets_set_genre': {\n const name = str(args.name)\n const description = str(args.description)\n if (name === '') throw new Error('assets_set_genre 需要 name')\n if (project.assets === undefined) project.assets = emptyProjectAssets()\n project.assets.genre = { name, description, children: [] }\n project.assets.updatedAt = new Date().toISOString()\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `题材已设为「${name}」`\n }\n case 'assets_set_progression': {\n const name = str(args.name)\n const driver = str(args.driver)\n const primary = args.primary !== false\n if (name === '') throw new Error('assets_set_progression 需要 name')\n if (project.assets === undefined) project.assets = emptyProjectAssets()\n const mode = {\n name,\n driver: driver !== '' ? driver : name,\n readerExpectation: str(args.readerExpectation),\n payoffs: Array.isArray(args.payoffs) ? args.payoffs.filter((v): v is string => typeof v === 'string') : [],\n risks: Array.isArray(args.risks) ? args.risks.filter((v): v is string => typeof v === 'string') : [],\n primary,\n }\n if (primary) project.assets.primaryProgression = mode\n else {\n if (project.assets.auxiliaryProgressions === undefined) project.assets.auxiliaryProgressions = []\n project.assets.auxiliaryProgressions.push(mode)\n }\n project.assets.updatedAt = new Date().toISOString()\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `推进模式${primary ? '(主)' : '(辅助)'}已设置:「${name}」`\n }\n case 'assets_add_rule': {\n const name = str(args.name)\n const avoid = str(args.avoid)\n if (avoid === '') throw new Error('assets_add_rule 需要 avoid(要避免的表达问题)')\n if (project.assets === undefined) project.assets = emptyProjectAssets()\n if (project.assets.antiAiRules === undefined) project.assets.antiAiRules = []\n project.assets.antiAiRules.push({\n name: name !== '' ? name : `自定义规则 ${project.assets.antiAiRules.length + 1}`,\n avoid,\n fix: str(args.fix),\n })\n project.assets.updatedAt = new Date().toISOString()\n project.updatedAt = new Date().toISOString()\n saveProject(outputDir, project)\n return `已新增反 AI 规则「${name !== '' ? name : avoid.slice(0, 20)}」`\n }\n default:\n throw new Error(`未知工具 ${name}`)\n }\n}\n\n// ------------------------------------------------------------------- chat\n\n/** Extract the first action directive from a reply (tolerant to common tag misspellings). */\nfunction extractAction(reply: string): { name: string; args: Record<string, unknown>; index: number } | undefined {\n // 容错:dsh-action 的常见误拼(dash-action / dsah-action / dhs-action 等)。\n const match = /<([a-z_]*d[a-z]?sh?-action)\\s+name=\"([^\"]+)\"\\s*>([\\s\\S]*?)<\\/\\1>/.exec(reply)\n if (match === null) return undefined\n const rawArgs = match[3]?.trim() ?? ''\n let args: Record<string, unknown>\n try {\n args = rawArgs === '' ? {} : JSON.parse(rawArgs) as Record<string, unknown>\n } catch {\n throw new Error(`动作参数不是合法 JSON:${rawArgs.slice(0, 80)}`)\n }\n return { name: match[2] ?? '', args, index: match.index }\n}\n\n/** Render the recent history as LLM messages (skipping tool chatter in early rounds). */\nfunction historyToMessages(history: AssistantMessage[]): Message[] {\n const recent = history.slice(-MAX_HISTORY_MESSAGES)\n const messages: Message[] = []\n for (const entry of recent) {\n if (entry.role === 'user') {\n messages.push(createUserMessage({\n content: [{ type: 'text', text: entry.content }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n }))\n } else if (entry.role === 'assistant') {\n messages.push(createAssistantMessage({\n content: [{ type: 'text', text: entry.content }],\n source: { provider: 'deepseek-official', model: 'deepseek-v4-flash' },\n }))\n } else if (entry.role === 'tool') {\n // 工具结果(book_overview/大纲全文等可能很大)截断后进上下文,\n // 避免多轮对话上下文翻倍膨胀触发「上下文到限制」。\n const body = entry.content.length > 2000\n ? entry.content.slice(0, 2000) + '\\n…(结果过长已截断,需要完整内容请重新调用工具)'\n : entry.content\n messages.push(createUserMessage({\n content: [{ type: 'text', text: `【工具 ${entry.tool ?? ''} 的执行结果】\\n${body}` }],\n source: { kind: 'plugin', plugin: 'dsh-novel-forge' },\n }))\n }\n }\n return messages\n}\n\n/** One non-streaming LLM chat turn (used inside the tool loop). */\nasync function chatOnce(\n ctx: Context,\n config: NovelConfig,\n system: string,\n history: AssistantMessage[],\n): Promise<string> {\n const messages = historyToMessages(history)\n const request: GenerateOptions = {\n provider: config.provider,\n model: config.model,\n messages,\n system,\n // v4-flash 推理模型:reasoning channel 占预算,给足避免回复被截断。\n maxTokens: Math.max(config.maxTokens, 16000),\n temperature: 0.7,\n }\n const assembler = new BlockAssembler()\n for await (const chunk of ctx.llm.stream(request)) {\n assembler.push(chunk)\n }\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n throw new Error(`助手调用失败(${finish.kind}): ${finish.failure.message}`)\n }\n const blocks = assembler.blocks()\n const textBlocks = blocks\n .filter((block): block is Extract<StreamChunk, { type: 'block-end' }>['block'] & { type: 'text' } => block.type === 'text')\n .map(block => block.text)\n let text = textBlocks.join('\\n').trim()\n if (text === '') {\n const reasoning = blocks\n .filter((block): block is { type: 'reasoning'; text: string } => block.type === 'reasoning')\n .map(block => block.text)\n .join('\\n')\n .trim()\n if (reasoning !== '') text = reasoning\n }\n return text\n}\n\n/** Run one user turn. Yields stream frames; persists history. */\nexport async function* runAssistantTurn(\n ctx: Context,\n config: NovelConfig,\n project: ProjectState,\n outputDir: string,\n userMessage: string,\n): AsyncGenerator<\n | { frame: 'delta'; text: string }\n | { frame: 'tool'; name: string; status: 'start' | 'done' | 'error'; detail?: string }\n | { frame: 'toolDelta'; name: string; text: string },\n void,\n unknown\n> {\n const history = loadAssistantHistory(outputDir)\n const system = assistantSystemPrompt(project)\n\n // Persist the user message.\n const userEntry: AssistantMessage = { role: 'user', content: userMessage, ts: new Date().toISOString() }\n history.push(userEntry)\n appendHistory(outputDir, userEntry)\n\n let round = 0\n /** Whether we already nudged the model to emit an action tag (avoid loops). */\n let nudged = false\n for (;;) {\n const reply = await chatOnce(ctx, config, system, history)\n const action = extractAction(reply)\n\n if (action === undefined) {\n // No parseable action tag. If the reply clearly intends to modify\n // something (or contains a malformed action tag), nudge once.\n const intendsAction = /(改|修改|修订|重写|替换|调整|生成|新增|删除|导出|看看|查看|调出|读一下|加上|加一个|去掉|删掉|把.+改成)/.test(reply)\n const strayTag = /<[a-z_-]*action[^>]*>/.test(reply)\n if ((intendsAction || strayTag) && !nudged) {\n nudged = true\n const nudge = '你的上一条回复表达了想操作项目的意图(或动作标签格式有误),因此没有执行任何操作。请直接输出动作标签来执行,格式必须为 <dsh-action name=\"工具名\">{\"参数\":值}</dsh-action>(注意拼写是 dsh-action,不是 dash-action;标签成对出现,参数为合法 JSON)。如果需要先看内容,先输出 outline_text 或 chapter_text 标签。'\n history.push({ role: 'tool', content: nudge, tool: 'format-hint', ts: new Date().toISOString() })\n appendHistory(outputDir, { role: 'tool', content: nudge, tool: 'format-hint', ts: new Date().toISOString() })\n continue\n }\n // Plain prose reply — done.\n const assistantEntry: AssistantMessage = { role: 'assistant', content: reply, ts: new Date().toISOString() }\n history.push(assistantEntry)\n appendHistory(outputDir, assistantEntry)\n // Stream the prose (without any stray action markup).\n yield { frame: 'delta', text: reply }\n return\n }\n\n // Execute the action, then feed the result back and continue.\n const { name, args, index } = action\n const prose = reply.slice(0, index).trim()\n yield { frame: 'tool', name, status: 'start' }\n let result: string\n try {\n // executeAction is an async generator: it yields live progress text\n // (chapter text being generated) and returns the final result string.\n // Iterate manually so we see both the yielded deltas and the return.\n const iterator = executeAction(ctx, config, project, outputDir, name, args)[Symbol.asyncIterator]()\n result = ''\n for (;;) {\n const step = await iterator.next()\n if (step.done === true) {\n result = typeof step.value === 'string' ? step.value : ''\n break\n }\n const chunk = step.value\n if (typeof chunk === 'string' && chunk !== '') {\n yield { frame: 'toolDelta', name, text: chunk }\n }\n }\n yield { frame: 'tool', name, status: 'done', detail: result.slice(0, 200) }\n } catch (error) {\n result = `执行失败:${(error as Error).message}`\n yield { frame: 'tool', name, status: 'error', detail: (error as Error).message }\n }\n\n // Persist assistant prose + tool result as history entries.\n if (prose !== '') {\n history.push({ role: 'assistant', content: prose, ts: new Date().toISOString() })\n appendHistory(outputDir, { role: 'assistant', content: prose, ts: new Date().toISOString() })\n }\n history.push({ role: 'tool', content: result, tool: name, ts: new Date().toISOString() })\n appendHistory(outputDir, { role: 'tool', content: result, tool: name, ts: new Date().toISOString() })\n\n round++\n if (round >= MAX_TOOL_ROUNDS) {\n const message = `(已连续执行 ${round} 次修改操作,本轮停止。如需继续请再说。)`\n history.push({ role: 'assistant', content: message, ts: new Date().toISOString() })\n appendHistory(outputDir, { role: 'assistant', content: message, ts: new Date().toISOString() })\n yield { frame: 'delta', text: message }\n return\n }\n }\n}\n\nexport { chapterFileName }\n","/**\n * 书架(Bookshelf)— 多书管理:一本书记录一个独立输出目录。\n * 状态持久化到 ~/.dsh/dsh-novel-forge-bookshelf.json(跟随 dsh 配置惯例)。\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { randomBytes } from 'node:crypto'\nimport type { BookEntry, BookshelfSnapshot } from './protocol.ts'\nimport { loadProject } from './engine.ts'\n\n/** 书架配置文件路径。 */\nexport function bookshelfFile(): string {\n return join(homedir(), '.dsh', 'dsh-novel-forge-bookshelf.json')\n}\n\ninterface BookshelfStore {\n books: BookEntry[]\n activeBookId: string | null\n}\n\nfunction defaultStore(): BookshelfStore {\n return { books: [], activeBookId: null }\n}\n\n/** 读取书架(无则返回空)。 */\nexport function loadBookshelf(): BookshelfStore {\n const file = bookshelfFile()\n if (!existsSync(file)) return defaultStore()\n try {\n let raw = readFileSync(file, 'utf8')\n if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1)\n const parsed = JSON.parse(raw) as BookshelfStore\n if (!Array.isArray(parsed.books)) return defaultStore()\n return { books: parsed.books, activeBookId: parsed.activeBookId ?? null }\n } catch {\n return defaultStore()\n }\n}\n\n/** 持久化书架。 */\nfunction saveBookshelf(store: BookshelfStore): void {\n const file = bookshelfFile()\n mkdirSync(join(homedir(), '.dsh'), { recursive: true })\n writeFileSync(file, JSON.stringify(store, null, 2), 'utf8')\n}\n\n/** 当前激活的书。 */\nexport function activeBook(store: BookshelfStore): BookEntry | undefined {\n return store.books.find(b => b.id === store.activeBookId)\n}\n\n/** 书架快照(含每本书的进度摘要)。 */\nexport function bookshelfSnapshot(store: BookshelfStore): BookshelfSnapshot {\n return {\n books: store.books.map(book => {\n const project = loadProject(book.outputDir)\n const done = project === undefined ? 0 : project.chapters.filter(c => c.status === 'approved' || c.status === 'written' || c.status === 'rejected').length\n const hasCover = project?.coverPath !== undefined && project.coverPath !== '' && existsSync(join(book.outputDir, project.coverPath))\n return {\n ...book,\n done,\n total: project?.chapters.length ?? 0,\n hasProject: project !== undefined,\n hasCover,\n blurb: project?.blurb,\n }\n }),\n activeBookId: store.activeBookId,\n }\n}\n\n/** 新建一本书(自动成为当前书)。 */\nexport function createBook(bookName: string, outputDir: string): BookEntry {\n const store = loadBookshelf()\n const id = `book-${Date.now().toString(36)}-${randomBytes(3).toString('hex')}`\n const now = new Date().toISOString()\n const book: BookEntry = { id, bookName, outputDir, createdAt: now, updatedAt: now }\n store.books.push(book)\n store.activeBookId = id\n saveBookshelf(store)\n return book\n}\n\n/** 更新某本书的书名(开书向导导入大纲后书名以大纲首行为准)。 */\nexport function renameBook(id: string, bookName: string): boolean {\n const store = loadBookshelf()\n const book = store.books.find(b => b.id === id)\n if (book === undefined) return false\n book.bookName = bookName\n book.updatedAt = new Date().toISOString()\n saveBookshelf(store)\n return true\n}\n\n/**\n * 播种:书架为空时,把指定输出目录下已有的项目自动登记为第一本书。\n * 兼容升级场景 —— 旧版插件直接在输出目录写项目,从未登记书架。\n * @param outputDir - 候选输出目录(通常为 settings 的默认输出目录)。\n * @returns 是否发生了播种。\n */\nexport function seedBookshelfFromOutputDir(outputDir: string): boolean {\n const store = loadBookshelf()\n if (store.books.length > 0) return false\n if (!existsSync(outputDir)) return false\n // 有项目文件,或至少有章节文件,才视为\"已有的书\"。\n const hasProject = existsSync(join(outputDir, 'novel-project.json'))\n const hasChapters = existsSync(outputDir)\n if (!hasProject && !hasChapters) return false\n const project = loadProject(outputDir)\n const bookName = project?.bookName ?? outputDir.split(/[\\\\/]/).pop() ?? '未命名小说'\n createBook(bookName, outputDir)\n return true\n}\n\n/** 激活一本书。 */\nexport function activateBook(id: string): BookEntry | undefined {\n const store = loadBookshelf()\n const book = store.books.find(b => b.id === id)\n if (book === undefined) return undefined\n store.activeBookId = id\n book.updatedAt = new Date().toISOString()\n saveBookshelf(store)\n return book\n}\n\n/** 移除一本书。 */\nexport function removeBook(id: string): boolean {\n const store = loadBookshelf()\n const idx = store.books.findIndex(b => b.id === id)\n if (idx === -1) return false\n store.books.splice(idx, 1)\n if (store.activeBookId === id) {\n store.activeBookId = store.books[0]?.id ?? null\n }\n saveBookshelf(store)\n return true\n}\n\n/** 当前书输出目录(无书架则 undefined,回退 settings)。 */\nexport function activeBookOutputDir(): string | undefined {\n const book = activeBook(loadBookshelf())\n return book?.outputDir\n}\n\n/** 默认输出目录推断:桌面/书名。 */\nexport function defaultOutputDirFor(bookName: string): string {\n const clean = bookName.replace(/[\\\\/:*?\"<>|]/g, '').trim().slice(0, 40) || '未命名小说'\n return join(homedir(), 'Desktop', clean)\n}\n","/**\n * The /api/dsh-novel-forge route family: status, docx outline loading, LLM\n * story-bible extraction, volume planning, chapter planning, streaming\n * generation / rewrite / polish (NDJSON frames), review, summaries,\n * foreshadows, export, chapter reading, config patching, and opening the\n * output folder. Every route carries the same loopback-only trust fence as\n * the family plugins — these endpoints invoke the LLM and write files on the\n * host machine.\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { exec } from 'node:child_process'\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { WebRoute } from '@deepseek-ai/dsh-host-webserver'\nimport type { Context } from '@deepseek-ai/cordis'\nimport {\n NOVEL_API,\n type AssetsPatch,\n type AssetsResponse,\n type AssistantFrame,\n type AssistantHistoryResponse,\n type AssistantRequest,\n type AuditResponse,\n type BiblePatchRequest,\n type BibleRequest,\n type BlurbRequest,\n type CoverRequest,\n type CoverResponse,\n type RenameRequest,\n type WorldRequest,\n type BibleResponse,\n type BookActivateRequest,\n type BookCreateRequest,\n type BookRemoveRequest,\n type BookshelfSnapshot,\n type ChapterResponse,\n type ChapterPlan,\n type ChapterSaveResponse,\n type ChapterTextRequest,\n type ConfigPatch,\n type DraftDecisionRequest,\n type ExportRequest,\n type ExportResponse,\n type ForeshadowRequest,\n type ForeshadowResponse,\n type JobFrame,\n type LoadOutlineRequest,\n type LoadOutlineResponse,\n type NovelConfig,\n type PlanRequest,\n type PlanResponse,\n type PolishRequest,\n type ResetRequest,\n type ReviewReport,\n type ReviewRequest,\n type RewriteRequest,\n type StatusResponse,\n type StyleEngineRequest,\n type SummaryRequest,\n type VolumesRequest,\n type VolumesResponse,\n} from './protocol.ts'\nimport { readOutlineFromDocx } from './docx.ts'\nimport { clearAssistantHistory, loadAssistantHistory, runAssistantTurn } from './assistant.ts'\nimport { activateBook, bookshelfSnapshot, createBook, defaultOutputDirFor, loadBookshelf, removeBook, renameBook, seedBookshelfFromOutputDir } from './bookshelf.ts'\nimport { BUILTIN_ANTI_AI_RULES, BUILTIN_GENRE_LIBRARY, BUILTIN_PROGRESSION_MODES, BUILTIN_STYLE_TEMPLATES, emptyProjectAssets } from './assets.ts'\nimport {\n chapterFileName,\n auditBook,\n backfillFacts,\n createProject,\n exportBook,\n extractBible,\n extractStyleAsset,\n extractWorld,\n generateBlurb,\n generateChapterStream,\n listChapterFiles,\n loadProject,\n planChapters,\n planVolumes,\n polishChapterStream,\n readChapterFile,\n refreshCharacters,\n reviewChapter,\n reviewChapterText,\n rewriteChapterStream,\n saveProject,\n suggestForeshadows,\n summarizeAndExtractFacts,\n summarizeChapter,\n syncProjectWithDisk,\n} from './engine.ts'\n\n/** Cap on JSON request bodies (generous: cover images travel as base64). */\nconst MAX_JSON_BODY_BYTES = 16 * 1024 * 1024\n\n/** Loopback-only fence (mirrors the family plugins' pairing routes). */\nfunction isLoopbackRequest(request: IncomingMessage): boolean {\n const address = request.socket.remoteAddress\n if (address !== '127.0.0.1' && address !== '::1' && address !== '::ffff:127.0.0.1') return false\n const host = request.headers.host\n if (typeof host !== 'string') return false\n let hostUrl: URL\n try {\n hostUrl = new URL(`http://${host}`)\n } catch {\n return false\n }\n if (hostUrl.hostname !== '127.0.0.1' && hostUrl.hostname !== 'localhost' && hostUrl.hostname !== '[::1]') return false\n if (request.headers['sec-fetch-site'] === 'cross-site') return false\n const origin = request.headers.origin\n if (origin === undefined) return true\n try {\n return new URL(origin).host === hostUrl.host\n } catch {\n return false\n }\n}\n\n/** One JSON response. */\nfunction writeJson(res: ServerResponse, status: number, body: unknown): void {\n const payload = JSON.stringify(body)\n res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'referrer-policy': 'no-referrer' })\n res.end(payload)\n}\n\n/** Read a JSON request body. */\nasync function readJsonBody<T>(req: IncomingMessage): Promise<T | undefined> {\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of req) {\n const buffer = chunk as Buffer\n size += buffer.length\n if (size > MAX_JSON_BODY_BYTES) return undefined\n chunks.push(buffer)\n }\n try {\n return JSON.parse(Buffer.concat(chunks).toString('utf8')) as T\n } catch {\n return undefined\n }\n}\n\n/** Route deps. */\nexport interface NovelRoutesDeps {\n ctx: Context\n /** Resolve the live plugin config (settings-aware). */\n getConfig: () => NovelConfig\n /** Persist a config patch through the settings seam. */\n patchConfig: (patch: ConfigPatch) => Promise<NovelConfig>\n}\n\n/** Default chapter count for planning when the request omits it. */\nconst DEFAULT_PLAN_COUNT = 30\n\n/**\n * Build every /api/dsh-novel-forge route.\n * @param deps - context, config resolver, config patcher.\n * @returns the route list.\n */\nexport function makeRoutes(deps: NovelRoutesDeps): WebRoute[] {\n const { ctx, getConfig, patchConfig } = deps\n\n /** Guard helper: fence + method check. */\n const guard = (req: IncomingMessage, res: ServerResponse, method: string): boolean => {\n if (!isLoopbackRequest(req)) {\n writeJson(res, 403, { error: 'forbidden: loopback-only' })\n return false\n }\n if (req.method !== method) {\n writeJson(res, 405, { error: `method not allowed (expected ${method})` })\n return false\n }\n return true\n }\n\n /** Load (and sync) the project, or respond 400. */\n const requireProject = (res: ServerResponse): ReturnType<typeof loadProject> => {\n const config = getConfig()\n const project = loadProject(config.outputDir)\n if (project === undefined) {\n writeJson(res, 400, { error: '输出目录中没有项目,请先加载大纲' })\n return undefined\n }\n syncProjectWithDisk(project, config.outputDir)\n saveProject(config.outputDir, project)\n return project\n }\n\n // -------------------------------------------------------------- status\n const statusRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.status,\n handler: (req, res) => {\n if (!guard(req, res, 'GET')) return\n const config = getConfig()\n // 书架为空时播种 settings 默认输出目录里的已有项目。\n seedBookshelfFromOutputDir(config.outputDir)\n const project = loadProject(config.outputDir)\n if (project !== undefined) {\n syncProjectWithDisk(project, config.outputDir)\n saveProject(config.outputDir, project)\n }\n const response: StatusResponse = {\n config,\n // 瘦身:facts 只回最近 80 条(客户端最多展示 60),避免长篇后\n // status 响应体随编年录无限膨胀。\n project: project !== undefined\n ? { ...project, facts: (project.facts ?? []).slice(-80) }\n : undefined,\n generatedFiles: listChapterFiles(config.outputDir),\n }\n writeJson(res, 200, response)\n },\n }\n\n // -------------------------------------------------------- load-outline\n const loadOutlineRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.loadOutline,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<LoadOutlineRequest>(req)\n const config = getConfig()\n try {\n let outline: string\n let path: string | undefined\n if (body?.text !== undefined && body.text.trim() !== '') {\n outline = body.text.trim()\n } else {\n const target = body?.path?.trim() !== '' && body?.path !== undefined ? body.path : config.outlinePath\n outline = readOutlineFromDocx(target)\n path = target\n }\n if (outline.length < 50) {\n writeJson(res, 400, { error: '大纲内容过短(<50 字符),请检查文件或直接粘贴大纲文本' })\n return\n }\n const response: LoadOutlineResponse = {\n outline,\n bookName: createProject(outline).bookName,\n chars: outline.length,\n path,\n }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 400, { error: (error as Error).message })\n }\n },\n }\n\n // -------------------------------------------------------- save-outline\n const saveOutlineRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.saveOutline,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<LoadOutlineRequest>(req)\n const config = getConfig()\n const outline = body?.text ?? ''\n if (outline.trim().length < 50) {\n writeJson(res, 400, { error: '大纲内容过短(<50 字符)' })\n return\n }\n let project = loadProject(config.outputDir)\n const now = new Date().toISOString()\n if (project === undefined) {\n project = createProject(outline)\n } else {\n project.outline = outline\n project.bookName = createProject(outline).bookName\n project.updatedAt = now\n }\n saveProject(config.outputDir, project)\n writeJson(res, 200, { ok: true, bookName: project.bookName })\n },\n }\n\n // --------------------------------------------------------------- bible\n const bibleRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.bible,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<BibleRequest>(req)\n const config = getConfig()\n const project = loadProject(config.outputDir)\n const outline = body?.outline?.trim() !== '' && body?.outline !== undefined\n ? body.outline\n : project?.outline\n if (outline === undefined || outline.length < 50) {\n writeJson(res, 400, { error: '请先加载大纲' })\n return\n }\n try {\n const bible = await extractBible(ctx, config, outline)\n const now = new Date().toISOString()\n const next = project ?? createProject(outline)\n next.bible = bible\n next.updatedAt = now\n saveProject(config.outputDir, next)\n const response: BibleResponse = { bible }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ------------------------------------------------------------- volumes\n const volumesRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.volumes,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<VolumesRequest>(req)\n const config = getConfig()\n const project = loadProject(config.outputDir)\n const outline = body?.outline?.trim() !== '' && body?.outline !== undefined\n ? body.outline\n : project?.outline\n if (outline === undefined || outline.length < 50) {\n writeJson(res, 400, { error: '请先加载大纲' })\n return\n }\n try {\n const volumes = await planVolumes(ctx, config, outline)\n const now = new Date().toISOString()\n const next = project ?? createProject(outline)\n next.volumes = volumes\n next.updatedAt = now\n saveProject(config.outputDir, next)\n const response: VolumesResponse = { volumes }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ----------------------------------------------------------------- plan\n const planRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.plan,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<PlanRequest>(req)\n const config = getConfig()\n const project = loadProject(config.outputDir)\n const outline = body?.outline?.trim() !== '' && body?.outline !== undefined\n ? body.outline\n : project?.outline\n if (outline === undefined || outline.length < 50) {\n writeJson(res, 400, { error: '请先加载大纲(或粘贴大纲文本)' })\n return\n }\n const count = body?.chapterCount ?? DEFAULT_PLAN_COUNT\n if (!Number.isInteger(count) || count < 1 || count > 200) {\n writeJson(res, 400, { error: 'chapterCount 须为 1-200 的整数' })\n return\n }\n try {\n const next = project ?? createProject(outline)\n const chapters: ChapterPlan[] = await planChapters(ctx, config, next, count, body?.volume, config.outputDir)\n next.chapters.push(...chapters)\n next.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, next)\n const response: PlanResponse = { chapters, volumes: next.volumes }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ------------------------------------------------------------- generate\n const generateRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.generate,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<{ chapterNo?: number; skipReview?: boolean }>(req)\n const rawNo = body?.chapterNo\n if (!Number.isInteger(rawNo) || rawNo === undefined || rawNo < 1) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const no: number = rawNo\n const chapter = project.chapters.find(c => c.no === no)\n if (chapter === undefined) {\n writeJson(res, 404, { error: `章节 ${no} 不在计划中` })\n return\n }\n if (chapter.status === 'generating') {\n writeJson(res, 409, { error: `章节 ${no} 正在生成中` })\n return\n }\n\n // NDJSON stream.\n res.writeHead(200, {\n 'content-type': 'application/x-ndjson; charset=utf-8',\n 'cache-control': 'no-cache',\n 'x-accel-buffering': 'no',\n 'referrer-policy': 'no-referrer',\n })\n chapter.status = 'generating'\n chapter.error = undefined\n saveProject(config.outputDir, project)\n\n const send = (frame: JobFrame): void => {\n res.write(JSON.stringify(frame) + '\\n')\n }\n\n try {\n send({ type: 'start', no, title: chapter.title })\n for await (const step of generateChapterStream(ctx, config, project, config.outputDir, no)) {\n if (step.frame === 'delta') {\n send({ type: 'delta', text: step.text })\n } else if (step.frame === 'done') {\n send({ type: 'done', no, file: step.file, chars: step.chars, title: chapter.title })\n }\n }\n // Auto pipeline: summary + facts(一次调用)-> review(unless skipped)。\n try {\n await summarizeAndExtractFacts(ctx, config, project, config.outputDir, no)\n } catch (error) {\n console.warn('[dsh-novel-forge] summary/facts failed:', (error as Error).message)\n }\n if (!(body?.skipReview === true) && (config.autoReview ?? true)) {\n const report = await reviewChapter(ctx, config, project, config.outputDir, no)\n send({ type: 'review', no, report })\n } else {\n chapter.status = 'approved'\n saveProject(config.outputDir, project)\n }\n res.end()\n } catch (error) {\n chapter.status = 'error'\n chapter.error = (error as Error).message\n saveProject(config.outputDir, project)\n if (!res.writableEnded) {\n send({ type: 'error', no, message: (error as Error).message })\n res.end()\n }\n }\n },\n }\n\n // --------------------------------------------------------------- review\n const reviewRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.review,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<ReviewRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const no = body!.chapterNo!\n try {\n const report = await reviewChapter(ctx, config, project, config.outputDir, no)\n writeJson(res, 200, { report })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // -------------------------------------------------------------- rewrite\n const rewriteRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.rewrite,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<RewriteRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const no = body!.chapterNo!\n res.writeHead(200, {\n 'content-type': 'application/x-ndjson; charset=utf-8',\n 'cache-control': 'no-cache',\n 'x-accel-buffering': 'no',\n 'referrer-policy': 'no-referrer',\n })\n const send = (frame: JobFrame): void => { res.write(JSON.stringify(frame) + '\\n') }\n try {\n for await (const step of rewriteChapterStream(ctx, config, project, config.outputDir, no, body?.instructions ?? '', body?.target)) {\n if (step.frame === 'delta') send({ type: 'delta', text: step.text })\n else if (step.frame === 'drafted') send({ type: 'drafted', no, chars: step.chars, draft: step.draft })\n }\n // Draft mode: no auto re-review — the user reviews the diff and\n // decides; re-run review after applying if wanted.\n res.end()\n } catch (error) {\n if (!res.writableEnded) {\n send({ type: 'error', no, message: (error as Error).message })\n res.end()\n }\n }\n },\n }\n\n // --------------------------------------------------------------- polish\n const polishRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.polish,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<PolishRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const no = body!.chapterNo!\n res.writeHead(200, {\n 'content-type': 'application/x-ndjson; charset=utf-8',\n 'cache-control': 'no-cache',\n 'x-accel-buffering': 'no',\n 'referrer-policy': 'no-referrer',\n })\n const send = (frame: JobFrame): void => { res.write(JSON.stringify(frame) + '\\n') }\n try {\n for await (const step of polishChapterStream(ctx, config, project, config.outputDir, no)) {\n if (step.frame === 'delta') send({ type: 'delta', text: step.text })\n else if (step.frame === 'drafted') send({ type: 'drafted', no, chars: step.chars, draft: step.draft })\n }\n res.end()\n } catch (error) {\n if (!res.writableEnded) {\n send({ type: 'error', no, message: (error as Error).message })\n res.end()\n }\n }\n },\n }\n\n // ---------------------------------------------------- draft apply/discard\n /** 采纳待确认草稿:覆盖正文文件 + 状态回 written + 清空草稿。 */\n const draftApplyRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.draftApply,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<DraftDecisionRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const chapter = project.chapters.find(c => c.no === body!.chapterNo!)\n if (chapter === undefined) {\n writeJson(res, 404, { error: `章节 ${body!.chapterNo} 不在计划中` })\n return\n }\n if (chapter.pendingDraft === undefined || chapter.pendingDraft === '') {\n writeJson(res, 400, { error: `章节 ${chapter.no} 没有待确认的草稿` })\n return\n }\n const draft = chapter.pendingDraft\n const fileName = chapterFileName(chapter)\n mkdirSync(config.outputDir, { recursive: true })\n const targetPath = join(config.outputDir, fileName)\n // 采纳前自动备份当前原稿为 .bak.md(每次应用都刷新为最新原稿),可随时回退。\n if (existsSync(targetPath)) {\n copyFileSync(targetPath, join(config.outputDir, `${fileName.replace(/\\.md$/, '')}.bak.md`))\n }\n writeFileSync(targetPath, `# 第${chapter.no}章 ${chapter.title}\\n\\n${draft}\\n`, 'utf8')\n chapter.pendingDraft = undefined\n chapter.status = 'written'\n chapter.chars = draft.length\n chapter.file = fileName\n chapter.review = undefined\n chapter.error = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { ok: true, chars: draft.length, file: fileName })\n },\n }\n\n /** 放弃待确认草稿:保留原稿,仅清空草稿字段。 */\n const draftDiscardRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.draftDiscard,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<DraftDecisionRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const chapter = project.chapters.find(c => c.no === body!.chapterNo!)\n if (chapter === undefined) {\n writeJson(res, 404, { error: `章节 ${body!.chapterNo} 不在计划中` })\n return\n }\n if (chapter.pendingDraft !== undefined) {\n chapter.pendingDraft = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n }\n writeJson(res, 200, { ok: true })\n },\n }\n\n // -------------------------------------------------------------- summary\n const summaryRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.summary,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<SummaryRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n try {\n const summary = await summarizeChapter(ctx, config, project, config.outputDir, body!.chapterNo!)\n writeJson(res, 200, { summary })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ---------------------------------------------------------- foreshadow\n const foreshadowRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.foreshadow,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<ForeshadowRequest>(req)\n try {\n if (body?.suggest === true) {\n // AI suggestion pass: create several foreshadows from the outline.\n const created = await suggestForeshadows(ctx, config, project)\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n const response: ForeshadowResponse = { foreshadows: created }\n writeJson(res, 200, response)\n return\n }\n if (body?.id !== undefined) {\n // Update an existing foreshadow.\n const target = project.foreshadows.find(f => f.id === body.id)\n if (target === undefined) {\n writeJson(res, 404, { error: `伏笔 ${body.id} 不存在` })\n return\n }\n if (body.description !== undefined) target.description = body.description\n if (body.plantedChapter !== undefined) target.plantedChapter = body.plantedChapter\n if (body.targetChapter !== undefined) target.targetChapter = body.targetChapter\n if (body.status !== undefined) target.status = body.status\n if (body.resolvedNote !== undefined) target.resolvedNote = body.resolvedNote\n } else {\n // Create one manually.\n const description = body?.description?.trim()\n if (description === undefined || description === '') {\n writeJson(res, 400, { error: 'description 必填' })\n return\n }\n project.foreshadows.push({\n id: `fs-${Date.now().toString(36)}`,\n description,\n plantedChapter: body?.plantedChapter,\n targetChapter: body?.targetChapter,\n status: body?.status ?? 'planned',\n })\n }\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n const response: ForeshadowResponse = { foreshadows: project.foreshadows }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // -------------------------------------------------------------- export\n const exportRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.exportBook,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<ExportRequest>(req)\n const format = body?.format === 'md' ? 'md' : 'txt'\n try {\n const result = exportBook(config.outputDir, project, format)\n const response: ExportResponse = { ...result }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // -------------------------------------------------------------- chapter\n const chapterRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.chapter,\n handler: async (req, res) => {\n if (!guard(req, res, 'GET')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const url = new URL(req.url ?? '/', 'http://localhost')\n const rawNo = Number(url.searchParams.get('no') ?? '0')\n if (!Number.isInteger(rawNo) || rawNo < 1) {\n writeJson(res, 400, { error: 'no 须为正整数' })\n return\n }\n const chapter = project.chapters.find(c => c.no === rawNo)\n if (chapter === undefined) {\n writeJson(res, 404, { error: `章节 ${rawNo} 不在计划中` })\n return\n }\n const markdown = readChapterFile(config.outputDir, chapter)\n if (markdown === undefined) {\n writeJson(res, 404, { error: `章节 ${rawNo} 尚未生成` })\n return\n }\n const response: ChapterResponse = { no: chapter.no, title: chapter.title, markdown }\n writeJson(res, 200, response)\n },\n }\n\n // ------------------------------------------------------ chapter check/save\n /** 审查手动编辑的正文(不落盘,返回审稿报告)。 */\n const chapterCheckRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.chapterCheck,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<ChapterTextRequest>(req)\n const text = body?.text?.trim() ?? ''\n if (text.length < 50) {\n writeJson(res, 400, { error: '正文过短(<50 字),请先编辑内容' })\n return\n }\n try {\n const report = await reviewChapterText(ctx, config, project, text)\n writeJson(res, 200, { report })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n /** 保存手动编辑的正文(自动备份 .bak,状态回 written)。 */\n const chapterSaveRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.chapterSave,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<ChapterTextRequest>(req)\n if (!Number.isInteger(body?.chapterNo)) {\n writeJson(res, 400, { error: 'chapterNo 须为正整数' })\n return\n }\n const chapter = project.chapters.find(c => c.no === body!.chapterNo!)\n if (chapter === undefined) {\n writeJson(res, 404, { error: `章节 ${body!.chapterNo} 不在计划中` })\n return\n }\n const text = body?.text?.trim() ?? ''\n if (text.length < 50) {\n writeJson(res, 400, { error: '正文过短(<50 字),未保存' })\n return\n }\n const fileName = chapterFileName(chapter)\n mkdirSync(config.outputDir, { recursive: true })\n const targetPath = join(config.outputDir, fileName)\n if (existsSync(targetPath)) {\n copyFileSync(targetPath, join(config.outputDir, `${fileName.replace(/\\.md$/, '')}.bak.md`))\n }\n writeFileSync(targetPath, `# 第${chapter.no}章 ${chapter.title}\\n\\n${text}\\n`, 'utf8')\n chapter.status = 'written'\n chapter.chars = text.length\n chapter.file = fileName\n chapter.pendingDraft = undefined\n // 保存即审稿:携带工作区审查报告则沿用(不重复审);否则自动正式审稿一次。\n let report: ReviewReport | undefined\n const carried = body?.report\n if (carried !== undefined && typeof carried.score === 'number') {\n report = carried\n chapter.review = report\n chapter.status = report.passed ? 'approved' : 'rejected'\n } else {\n report = await reviewChapter(ctx, config, project, config.outputDir, chapter.no)\n }\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n const response: ChapterSaveResponse = { ok: true, chars: text.length, file: fileName, report }\n writeJson(res, 200, response)\n },\n }\n\n // ----------------------------------------------------------- assistant\n const assistantRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.assistant,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<AssistantRequest>(req)\n const message = body?.message?.trim()\n if (message === undefined || message === '') {\n writeJson(res, 400, { error: '消息不能为空' })\n return\n }\n res.writeHead(200, {\n 'content-type': 'application/x-ndjson; charset=utf-8',\n 'cache-control': 'no-cache',\n 'x-accel-buffering': 'no',\n 'referrer-policy': 'no-referrer',\n })\n const send = (frame: AssistantFrame): void => { res.write(JSON.stringify(frame) + '\\n') }\n try {\n for await (const step of runAssistantTurn(ctx, config, project, config.outputDir, message)) {\n if (step.frame === 'delta') send({ type: 'delta', text: step.text })\n else if (step.frame === 'tool') send({ type: 'tool', name: step.name, status: step.status, detail: step.detail })\n else if (step.frame === 'toolDelta') send({ type: 'toolDelta', name: step.name, text: step.text })\n }\n send({ type: 'done' })\n res.end()\n } catch (error) {\n if (!res.writableEnded) {\n send({ type: 'error', message: (error as Error).message })\n res.end()\n }\n }\n },\n }\n\n // -------------------------------------------------- assistant-history\n const assistantHistoryRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.assistantHistory,\n handler: (req, res) => {\n if (!guard(req, res, 'GET')) return\n const config = getConfig()\n const response: AssistantHistoryResponse = { messages: loadAssistantHistory(config.outputDir) }\n writeJson(res, 200, response)\n },\n }\n\n // --------------------------------------------------- assistant-clear\n /** 清空助手对话记录。 */\n const assistantClearRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.assistantClear,\n handler: (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n clearAssistantHistory(config.outputDir)\n writeJson(res, 200, { ok: true })\n },\n }\n\n // --------------------------------------------------------------- assets\n const assetsRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.assets,\n handler: async (req, res) => {\n // GET (read) and POST (patch) are both allowed — check methods first,\n // then the loopback fence (guard() would 405 on POST, which is wrong).\n if (req.method !== 'GET' && req.method !== 'POST') {\n writeJson(res, 405, { error: 'method not allowed (expected GET or POST)' })\n return\n }\n if (!isLoopbackRequest(req)) {\n writeJson(res, 403, { error: 'forbidden: loopback-only' })\n return\n }\n const config = getConfig()\n const project = loadProject(config.outputDir)\n const projectAssets = project?.assets ?? emptyProjectAssets()\n if (req.method === 'POST') {\n const body = await readJsonBody<AssetsPatch>(req)\n if (body === undefined) {\n writeJson(res, 400, { error: '无效的 JSON' })\n return\n }\n if (project === undefined) {\n writeJson(res, 400, { error: '请先加载大纲创建项目' })\n return\n }\n if (body.genre !== undefined) projectAssets.genre = body.genre\n if (body.primaryProgression !== undefined) projectAssets.primaryProgression = body.primaryProgression\n if (body.auxiliaryProgressions !== undefined) projectAssets.auxiliaryProgressions = body.auxiliaryProgressions\n if (body.antiAiRules !== undefined) projectAssets.antiAiRules = body.antiAiRules\n if (body.styleAssets !== undefined) projectAssets.styleAssets = body.styleAssets\n projectAssets.updatedAt = new Date().toISOString()\n project.assets = projectAssets\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n }\n const response: AssetsResponse = {\n projectAssets,\n genreLibrary: BUILTIN_GENRE_LIBRARY,\n antiAiLibrary: BUILTIN_ANTI_AI_RULES,\n styleTemplates: BUILTIN_STYLE_TEMPLATES,\n progressionLibrary: BUILTIN_PROGRESSION_MODES,\n }\n writeJson(res, 200, response)\n },\n }\n\n // ----------------------------------------------------------- style-engine\n const styleEngineRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.styleEngine,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = loadProject(config.outputDir)\n const body = await readJsonBody<StyleEngineRequest>(req)\n const sample = body?.sampleText?.trim()\n if (sample === undefined || sample.length < 50) {\n writeJson(res, 400, { error: '样本文本过短(<50 字符),请粘贴一段能代表目标风格的文字' })\n return\n }\n try {\n const rules = await extractStyleAsset(ctx, config, sample)\n const name = body?.name?.trim() !== '' && body?.name !== undefined ? body.name : `风格资产 ${Date.now().toString(36)}`\n const styleAsset = {\n name: name.slice(0, 40),\n ...rules,\n sourceText: sample.slice(0, 3000),\n createdAt: new Date().toISOString(),\n }\n if (project !== undefined) {\n project.assets ??= emptyProjectAssets()\n project.assets.styleAssets ??= []\n project.assets.styleAssets.push(styleAsset)\n project.assets.updatedAt = new Date().toISOString()\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n }\n writeJson(res, 200, { styleAsset })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ------------------------------------------------------------- bookshelf\n const bookshelfRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.bookshelf,\n handler: async (req, res) => {\n // GET = snapshot; POST = create book.\n if (req.method === 'GET') {\n if (!isLoopbackRequest(req)) {\n writeJson(res, 403, { error: 'forbidden: loopback-only' })\n return\n }\n // 书架为空时,把 settings 默认输出目录里已有的项目播种为第一本书。\n seedBookshelfFromOutputDir(getConfig().outputDir)\n const snapshot: BookshelfSnapshot = bookshelfSnapshot(loadBookshelf())\n writeJson(res, 200, snapshot)\n return\n }\n if (req.method === 'POST') {\n if (!isLoopbackRequest(req)) {\n writeJson(res, 403, { error: 'forbidden: loopback-only' })\n return\n }\n const body = await readJsonBody<BookCreateRequest>(req)\n const bookName = body?.bookName?.trim()\n if (bookName === undefined || bookName === '') {\n writeJson(res, 400, { error: 'bookName 不能为空' })\n return\n }\n const outputDir = body?.outputDir?.trim() !== '' && body?.outputDir !== undefined\n ? body.outputDir\n : defaultOutputDirFor(bookName)\n const book = createBook(bookName, outputDir)\n // 开书向导:创建时带大纲 → 立即建立项目(书名以大纲首行为准)。\n const outline = body?.outline?.trim()\n if (outline !== undefined && outline.length >= 50) {\n const project = createProject(outline)\n saveProject(outputDir, project)\n renameBook(book.id, project.bookName)\n }\n writeJson(res, 200, bookshelfSnapshot(loadBookshelf()))\n return\n }\n writeJson(res, 405, { error: 'method not allowed (expected GET or POST)' })\n },\n }\n\n // --------------------------------------------------------------- reset\n /** 重置项目:清空设定/卷/章节计划/正文/伏笔/资产/事实库(可携带新大纲)。 */\n const resetRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.reset,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = loadProject(config.outputDir)\n if (project === undefined) {\n writeJson(res, 400, { error: '输出目录中没有项目,无需重置' })\n return\n }\n const body = await readJsonBody<ResetRequest>(req)\n const outline = body?.outline?.trim()\n if (outline !== undefined && outline.length >= 50) {\n project.outline = outline\n project.bookName = createProject(outline).bookName\n }\n project.bible = undefined\n project.volumes = undefined\n project.chapters = []\n project.foreshadows = []\n project.assets = emptyProjectAssets()\n project.facts = []\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { ok: true, bookName: project.bookName })\n },\n }\n\n // --------------------------------------------------- bookshelf activate\n const bookshelfActivateRoute: WebRoute = {\n kind: 'exact',\n path: '/api/dsh-novel-forge/bookshelf/activate',\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<BookActivateRequest>(req)\n if (body?.id === undefined || body.id === '') {\n writeJson(res, 400, { error: 'id 不能为空' })\n return\n }\n const book = activateBook(body.id)\n if (book === undefined) {\n writeJson(res, 404, { error: `书 ${body.id} 不存在` })\n return\n }\n writeJson(res, 200, bookshelfSnapshot(loadBookshelf()))\n },\n }\n\n // ---------------------------------------------------- bookshelf remove\n const bookshelfRemoveRoute: WebRoute = {\n kind: 'exact',\n path: '/api/dsh-novel-forge/bookshelf/remove',\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<BookRemoveRequest>(req)\n if (body?.id === undefined || body.id === '') {\n writeJson(res, 400, { error: 'id 不能为空' })\n return\n }\n const removed = removeBook(body.id)\n if (!removed) {\n writeJson(res, 404, { error: `书 ${body.id} 不存在` })\n return\n }\n writeJson(res, 200, bookshelfSnapshot(loadBookshelf()))\n },\n }\n\n // ---------------------------------------------------------------- audit\n /** 全书一致性质检。 */\n const auditRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.audit,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n try {\n const issues = await auditBook(ctx, config, project, config.outputDir)\n const response: AuditResponse = {\n issues,\n auditedChapters: project.chapters.filter(c => c.status !== 'pending' && c.status !== 'generating').length,\n auditedAt: new Date().toISOString(),\n }\n writeJson(res, 200, response)\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ----------------------------------------------------- characters refresh\n /** 角色卡刷新(出场统计精确化 + LLM 聚合状态)。 */\n const charactersRefreshRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.charactersRefresh,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n try {\n const cards = await refreshCharacters(ctx, config, project, config.outputDir)\n writeJson(res, 200, { cards })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ------------------------------------------------------- facts backfill\n /** 事实库回填:对历史已生成章节批量抽取事实。 */\n const factsBackfillRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.factsBackfill,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n try {\n const filled = await backfillFacts(ctx, config, project, config.outputDir)\n writeJson(res, 200, { ok: true, filled })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ---------------------------------------------------------- bible patch\n /** 设定圣经局部修补(世界观规则/红线/风格)。 */\n const biblePatchRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.biblePatch,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n if (project.bible === undefined) {\n writeJson(res, 400, { error: '尚未生成道藏,请先生成' })\n return\n }\n const body = await readJsonBody<BiblePatchRequest>(req)\n if (Array.isArray(body?.worldRules)) project.bible.worldRules = body.worldRules.filter(r => r.trim() !== '')\n if (Array.isArray(body?.redLines)) project.bible.redLines = body.redLines.filter(r => r.trim() !== '')\n if (Array.isArray(body?.style)) project.bible.style = body.style.filter(r => r.trim() !== '')\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { bible: project.bible })\n },\n }\n\n // ---------------------------------------------------------------- blurb\n /** 小说简介:AI 生成/补全,或手动保存。 */\n const blurbRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.blurb,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<BlurbRequest>(req)\n try {\n if (body?.action === 'save') {\n const text = body.text?.trim() ?? ''\n project.blurb = text\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { blurb: text })\n return\n }\n const partial = body?.partial?.trim() ?? ''\n const blurb = await generateBlurb(ctx, config, project, partial)\n project.blurb = blurb\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { blurb })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // ---------------------------------------------------------------- cover\n /** 封面:GET 读取(dataUrl);POST 上传(base64)或移除。 */\n const coverRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.cover,\n handler: async (req, res) => {\n const config = getConfig()\n if (req.method === 'GET') {\n if (!isLoopbackRequest(req)) {\n writeJson(res, 403, { error: 'forbidden: loopback-only' })\n return\n }\n // 书架页按书读取:?dir=<输出目录>;省略时用当前激活书目录。\n const url = new URL(req.url ?? '/', 'http://localhost')\n const dirParam = url.searchParams.get('dir')\n const targetDir = dirParam !== null && dirParam !== '' ? dirParam : config.outputDir\n const project = loadProject(targetDir)\n const coverPath = project?.coverPath\n if (coverPath === undefined || coverPath === '') {\n writeJson(res, 200, { dataUrl: null } satisfies CoverResponse)\n return\n }\n const file = join(targetDir, coverPath)\n if (!existsSync(file)) {\n writeJson(res, 200, { dataUrl: null } satisfies CoverResponse)\n return\n }\n const mime = coverPath.toLowerCase().endsWith('.png') ? 'image/png'\n : coverPath.toLowerCase().endsWith('.jpg') || coverPath.toLowerCase().endsWith('.jpeg') ? 'image/jpeg'\n : coverPath.toLowerCase().endsWith('.webp') ? 'image/webp'\n : 'image/png'\n const dataUrl = `data:${mime};base64,${readFileSync(file).toString('base64')}`\n writeJson(res, 200, { dataUrl } satisfies CoverResponse)\n return\n }\n if (req.method === 'POST') {\n if (!guard(req, res, 'POST')) return\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<CoverRequest>(req)\n try {\n if (body?.action === 'remove') {\n if (project.coverPath !== undefined && project.coverPath !== '') {\n const oldFile = join(config.outputDir, project.coverPath)\n if (existsSync(oldFile)) rmSync(oldFile, { force: true })\n }\n project.coverPath = undefined\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { ok: true, coverPath: null })\n return\n }\n const dataUrl = body?.dataUrl ?? ''\n const match = /^data:(image\\/(?:png|jpeg|jpg|webp));base64,(.+)$/s.exec(dataUrl)\n if (match === null) {\n writeJson(res, 400, { error: '封面须为 PNG/JPEG/WebP 的 base64 data URL' })\n return\n }\n const mime = match[1]!\n const ext = mime === 'image/png' ? 'png' : mime === 'image/webp' ? 'webp' : 'jpg'\n const fileName = `cover.${ext}`\n const targetPath = join(config.outputDir, fileName)\n const oldPath = project.coverPath\n mkdirSync(config.outputDir, { recursive: true })\n writeFileSync(targetPath, Buffer.from(match[2]!, 'base64'))\n if (oldPath !== undefined && oldPath !== '' && oldPath !== fileName) {\n const oldFile = join(config.outputDir, oldPath)\n if (existsSync(oldFile)) rmSync(oldFile, { force: true })\n }\n project.coverPath = fileName\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { ok: true, coverPath: fileName })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n return\n }\n writeJson(res, 405, { error: 'method not allowed (expected GET or POST)' })\n },\n }\n\n // ---------------------------------------------------------------- world\n /** 大世界:AI 提炼或手动保存(境界体系/区域/势力)。 */\n const worldRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.world,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<WorldRequest>(req)\n try {\n if (body?.action === 'save' && body.world !== undefined) {\n project.world = {\n realms: Array.isArray(body.world.realms) ? body.world.realms : [],\n regions: Array.isArray(body.world.regions) ? body.world.regions : [],\n factions: Array.isArray(body.world.factions) ? body.world.factions : [],\n }\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { world: project.world })\n return\n }\n const world = await extractWorld(ctx, config, project)\n project.world = world\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n writeJson(res, 200, { world })\n } catch (error) {\n writeJson(res, 500, { error: (error as Error).message })\n }\n },\n }\n\n // --------------------------------------------------------------- rename\n /** 重命名当前书:同步项目 bookName 与书架条目。 */\n const renameRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.rename,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const project = requireProject(res)\n if (project === undefined) return\n const body = await readJsonBody<RenameRequest>(req)\n const bookName = body?.bookName?.trim()\n if (bookName === undefined || bookName === '') {\n writeJson(res, 400, { error: '书名不能为空' })\n return\n }\n project.bookName = bookName.slice(0, 60)\n project.updatedAt = new Date().toISOString()\n saveProject(config.outputDir, project)\n const store = loadBookshelf()\n if (store.activeBookId !== null) renameBook(store.activeBookId, project.bookName)\n writeJson(res, 200, { bookName: project.bookName })\n },\n }\n\n // --------------------------------------------------------------- config\n const configRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.config,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const body = await readJsonBody<ConfigPatch>(req)\n if (body === undefined) {\n writeJson(res, 400, { error: '无效的配置 JSON' })\n return\n }\n try {\n const next = await patchConfig(body)\n writeJson(res, 200, { config: next })\n } catch (error) {\n writeJson(res, 400, { error: (error as Error).message })\n }\n },\n }\n\n // ---------------------------------------------------------- open-folder\n const openFolderRoute: WebRoute = {\n kind: 'exact',\n path: NOVEL_API.openFolder,\n handler: async (req, res) => {\n if (!guard(req, res, 'POST')) return\n const config = getConfig()\n const dir = config.outputDir\n exec(`explorer \"${dir.replace(/\"/g, '')}\"`, (error) => {\n if (error) {\n writeJson(res, 500, { ok: false, error: error.message })\n } else {\n writeJson(res, 200, { ok: true })\n }\n })\n },\n }\n\n return [\n statusRoute,\n loadOutlineRoute,\n saveOutlineRoute,\n bibleRoute,\n volumesRoute,\n planRoute,\n generateRoute,\n reviewRoute,\n rewriteRoute,\n polishRoute,\n draftApplyRoute,\n draftDiscardRoute,\n summaryRoute,\n foreshadowRoute,\n exportRoute,\n chapterRoute,\n chapterCheckRoute,\n chapterSaveRoute,\n assetsRoute,\n styleEngineRoute,\n assistantRoute,\n assistantHistoryRoute,\n assistantClearRoute,\n bookshelfRoute,\n bookshelfActivateRoute,\n bookshelfRemoveRoute,\n resetRoute,\n auditRoute,\n charactersRefreshRoute,\n factsBackfillRoute,\n biblePatchRoute,\n blurbRoute,\n coverRoute,\n worldRoute,\n renameRoute,\n configRoute,\n openFolderRoute,\n ]\n}\n\n// Re-export for tests / type consumers.\nexport type {\n ConfigPatch,\n NovelConfig,\n StatusResponse,\n PlanResponse,\n LoadOutlineResponse,\n BibleResponse,\n VolumesResponse,\n ExportResponse,\n}\nexport { chapterFileName }\n","/**\n * dsh-novel-forge — host half. Mounts the AI novel-forge workbench: docx\n * outline import, LLM chapter planning, chapter-by-chapter generation\n * (3000-4000 chars each), Markdown output into your chosen folder, and the\n * /api/dsh-novel-forge route family. The browser half (./client) renders the\n * workbench panel. Everything rides official NPM SDK packages — no dsh source\n * changes.\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'\nimport z from 'schemastery'\nimport type {} from '@deepseek-ai/dsh-host-webserver'\nimport type {} from '@deepseek-ai/dsh-llm'\nimport type {} from '@deepseek-ai/dsh-system-prompt'\nimport type { ConfigPatch, NovelConfig } from './protocol.ts'\nimport { makeRoutes } from './routes.ts'\nimport { activeBookOutputDir } from './bookshelf.ts'\n\n/** Stable cordis plugin name. */\nexport const name = 'novel-forge'\n\n/** Services required before the novel-forge surfaces can mount. */\nexport const inject = ['webServer', 'llm', 'systemPrompt']\n\n/**\n * Settings namespace of the novel-forge capability — the section the web\n * settings surface edits. Spelled here rather than imported: the browser half\n * spells the same value and must not depend on a Host package.\n */\nexport const NOVEL_SETTINGS_NAMESPACE = settingsNamespace('dsh-novel-forge')\n\n/** Plugin config, validated by the same-named schemastery schema. */\nexport interface Config {\n /** When true (default), a system-prompt section announces the plugin to every agent. */\n announceToAgent?: boolean\n /** Master switch for the plugin (routes, prompt section). */\n enabled?: boolean\n /** Absolute path of the default docx outline to load. */\n outlinePath?: string\n /** Absolute output directory for chapters + project state. */\n outputDir?: string\n /** LLM provider route. */\n provider?: string\n /** LLM model id. */\n model?: string\n /** Target characters per chapter. */\n chapterChars?: number\n /** Max output tokens per chapter call. */\n maxTokens?: number\n /** Review pass threshold (0-100). */\n reviewPassScore?: number\n /** Whether generation auto-runs review after writing. */\n autoReview?: boolean\n}\n\nexport const Config: z<Config> = z.object({\n announceToAgent: z.boolean().default(true),\n enabled: z.boolean().default(true),\n outlinePath: z.string().default('C:\\\\Users\\\\Ryan\\\\Desktop\\\\《归墟玉主》全书大纲_重新排版版.docx'),\n outputDir: z.string().default('C:\\\\Users\\\\Ryan\\\\Desktop\\\\归墟玉主'),\n provider: z.string().default('deepseek-official'),\n model: z.string().default('deepseek-v4-flash'),\n chapterChars: z.number().default(3500),\n maxTokens: z.number().default(12000),\n reviewPassScore: z.number().default(70),\n autoReview: z.boolean().default(true),\n})\n\n/** Schema defaults, re-read for hand-built test contexts. */\nconst DEFAULT_ANNOUNCE = true\nconst DEFAULT_OUTLINE_PATH = 'C:\\\\Users\\\\Ryan\\\\Desktop\\\\《归墟玉主》全书大纲_重新排版版.docx'\nconst DEFAULT_OUTPUT_DIR = 'C:\\\\Users\\\\Ryan\\\\Desktop\\\\归墟玉主'\nconst DEFAULT_PROVIDER = 'deepseek-official'\nconst DEFAULT_MODEL = 'deepseek-v4-flash'\nconst DEFAULT_CHAPTER_CHARS = 3500\nconst DEFAULT_MAX_TOKENS = 12000\nconst DEFAULT_REVIEW_PASS_SCORE = 70\nconst DEFAULT_AUTO_REVIEW = true\n\n/** Order of the announcement section within the tool-guidance band. */\nconst SECTION_ORDER = 160\n\n/** Model-facing announcement: plugin presence, capabilities, and limits. */\nexport const NOVEL_GUIDANCE = '本机已安装 dsh-novel-forge 插件(AI 编译小说工作台):侧边栏「小说工坊」入口。能力:读取 docx 大纲(默认桌面《归墟玉主》大纲)或粘贴大纲文本;用 LLM 提炼道藏(人设/世界观/金手指规则/写作红线,即设定圣经);生成卷计划与章节计划;逐章调用 LLM 生成 3000-4000 字正文并保存为 Markdown(默认输出到 桌面\\\\归墟玉主);每章自动生成摘要(叙事记忆)、自动 AI 审稿(人设/设定/红线/文笔/爽点/逻辑),支持按审稿意见重写、去 AI 味润色、暗线(伏笔)管理、批量连写与全本导出(txt/md)。限制:生成消耗 LLM API 额度;输出目录与模型可在插件设置中修改;章节正文质量取决于大纲完整度。用户提到「小说 / 大纲 / 写小说 / 章节 / 审稿 / 润色 / 归墟玉主」时即指本插件,请据此协作。'\n\n/** Resolve a config-like value into the full runtime config. */\nexport function resolveConfig(value: Partial<Config> | undefined): NovelConfig {\n return {\n outlinePath: value?.outlinePath ?? DEFAULT_OUTLINE_PATH,\n outputDir: value?.outputDir ?? DEFAULT_OUTPUT_DIR,\n provider: value?.provider ?? DEFAULT_PROVIDER,\n model: value?.model ?? DEFAULT_MODEL,\n chapterChars: value?.chapterChars ?? DEFAULT_CHAPTER_CHARS,\n maxTokens: value?.maxTokens ?? DEFAULT_MAX_TOKENS,\n reviewPassScore: value?.reviewPassScore ?? DEFAULT_REVIEW_PASS_SCORE,\n autoReview: value?.autoReview ?? DEFAULT_AUTO_REVIEW,\n }\n}\n\n/**\n * Mount the routes and announcement.\n * @param ctx - host plugin context carrying webServer/llm/systemPrompt.\n * @param config - resolved plugin config (schema defaults applied by the loader).\n */\nexport function apply(ctx: Context, config?: Config): void {\n // The live source the routes read: the settings section once the web\n // settings surface is served, the composition entry otherwise.\n let current: () => Config = () => config ?? {}\n const resolve = (): NovelConfig => {\n const resolved = resolveConfig(current())\n // 书架激活的书优先决定输出目录(settings 仍可改默认值)。\n const shelfDir = activeBookOutputDir()\n if (shelfDir !== undefined) {\n return { ...resolved, outputDir: shelfDir }\n }\n return resolved\n }\n\n const patchConfig = async (patch: ConfigPatch): Promise<NovelConfig> => {\n const next: ConfigPatch = {}\n if (patch.outlinePath !== undefined) next.outlinePath = patch.outlinePath\n if (patch.outputDir !== undefined) next.outputDir = patch.outputDir\n if (patch.provider !== undefined) next.provider = patch.provider\n if (patch.model !== undefined) next.model = patch.model\n if (patch.chapterChars !== undefined) next.chapterChars = patch.chapterChars\n if (patch.maxTokens !== undefined) next.maxTokens = patch.maxTokens\n if (patch.reviewPassScore !== undefined) next.reviewPassScore = patch.reviewPassScore\n if (patch.autoReview !== undefined) next.autoReview = patch.autoReview\n // Persist through the settings seam when available; otherwise keep in memory.\n // (ctx.get is the non-strict service access — no inject requirement, same\n // pattern installSettingsSection itself uses.)\n const settings = ctx.get('settings')\n if (settings !== undefined) {\n await settings.update(NOVEL_SETTINGS_NAMESPACE, next as Record<string, unknown>)\n } else {\n current = () => ({ ...current(), ...next })\n }\n return resolve()\n }\n\n let disposeSection: (() => void) | undefined\n let disposeRoutes: (() => void) | undefined\n\n const sync = (): void => {\n if (disposeSection !== undefined) {\n disposeSection()\n disposeSection = undefined\n }\n if (disposeRoutes !== undefined) {\n disposeRoutes()\n disposeRoutes = undefined\n }\n const value = resolve()\n if (!(current().enabled ?? true)) return\n if (current().announceToAgent ?? DEFAULT_ANNOUNCE) {\n disposeSection = ctx.systemPrompt.section({\n name: 'plugin:dsh-novel-forge',\n order: SECTION_ORDER,\n text: NOVEL_GUIDANCE,\n })\n }\n const routes = makeRoutes({ ctx, getConfig: resolve, patchConfig })\n disposeRoutes = ctx.effect(\n () => {\n const disposers = routes.map(route => ctx.webServer.register(route))\n return () => { for (const dispose of disposers) dispose() }\n },\n 'dsh-novel-forge: routes',\n )\n void value\n }\n\n installSettingsSection(ctx, NOVEL_SETTINGS_NAMESPACE, Config, config ?? {}, {\n setSource: (source) => {\n current = source\n sync()\n },\n onChange: sync,\n })\n\n // Initial registration from the composition entry.\n sync()\n}\n"],"mappings":"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ACF;EACA,eAAe,CACb,qBACA,aACF;EACA,eAAe,CACb,iBACA,iBACF;EACA,aAAa,CACX,qBACA,cACF;EACA,uBAAuB;GAAC;GAAU;GAAU;GAAW;EAAU;CACnE;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,UAAU;EACV,kBAAkB;GAAC;GAAM;GAAM;EAAI;EACnC,YAAY,CACV,gCACA,gBACF;EACA,eAAe,CACb,yBACF;EACA,eAAe,CACb,kBACA,WACF;EACA,aAAa,CACX,mBACA,SACF;EACA,uBAAuB;GAAC;GAAW;GAAW;EAAQ;CACxD;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,UAAU;EACV,kBAAkB;GAAC;GAAM;GAAM;EAAI;EACnC,YAAY,CACV,4BACA,iBACF;EACA,eAAe,CACb,oBACA,WACF;EACA,eAAe,CACb,oBACA,eACF;EACA,aAAa,CACX,gBACA,UACF;EACA,uBAAuB;GAAC;GAAU;GAAY;EAAU;CAC1D;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,UAAU;EACV,kBAAkB;GAAC;GAAM;GAAM;EAAI;EACnC,YAAY;GACV;GACA;GACA;EACF;EACA,eAAe,CACb,mBACA,gBACF;EACA,eAAe,CACb,mBACA,WACF;EACA,aAAa,CACX,iBACA,gBACF;EACA,uBAAuB;GAAC;GAAU;GAAU;EAAS;CACvD;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,UAAU;EACV,kBAAkB;GAAC;GAAM;GAAQ;EAAI;EACrC,YAAY;GACV;GACA;GACA;EACF;EACA,eAAe,CACb,gBACA,mBACF;EACA,eAAe,CACb,mBACA,aACF;EACA,aAAa,CACX,mBACA,gBACF;EACA,uBAAuB;GAAC;GAAa;GAAU;GAAU;EAAQ;CACnE;AACF;;AAKA,MAAa,wBAAsC;CACjD;EACE,MAAM;EACN,OAAO;EACP,KAAK;EACL,gBAAgB;GAAC;GAAO;GAAO;GAAQ;GAAQ;GAAQ;EAAM;EAC7D,SAAS;CACX;CACA;EACE,MAAM;EACN,OAAO;EACP,KAAK;EACL,gBAAgB;GAAC;GAAQ;G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"}