@waterwx/dsh-novel-forge 1.3.1 → 1.3.2-alpha

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.
Files changed (161) hide show
  1. package/README.md +288 -279
  2. package/lib/client.js +56309 -12221
  3. package/lib/index.js +14239 -5595
  4. package/lib/types/action-guard.d.ts +61 -0
  5. package/lib/types/ai-scan.d.ts +36 -0
  6. package/lib/types/assets.d.ts +24 -9
  7. package/lib/types/assistant.d.ts +29 -0
  8. package/lib/types/author-assets.d.ts +25 -0
  9. package/lib/types/bookshelf.d.ts +11 -1
  10. package/lib/types/client/api.d.ts +186 -10
  11. package/lib/types/client/chip-prefs.d.ts +36 -0
  12. package/lib/types/client/docx.d.ts +2 -0
  13. package/lib/types/client/index.d.ts +1 -1
  14. package/lib/types/client/llmLive.d.ts +31 -0
  15. package/lib/types/client/locales.d.ts +28 -67
  16. package/lib/types/client/panel/AdaptModeView.d.ts +5 -0
  17. package/lib/types/client/panel/AiTag.d.ts +1 -0
  18. package/lib/types/client/panel/AssistantTab.d.ts +4 -1
  19. package/lib/types/client/panel/AuthorAssetsView.d.ts +4 -0
  20. package/lib/types/client/panel/AuthorHome.d.ts +34 -0
  21. package/lib/types/client/panel/BookAnalysisView.d.ts +4 -0
  22. package/lib/types/client/panel/CmdPalette.d.ts +12 -0
  23. package/lib/types/client/panel/CreateBookView.d.ts +5 -1
  24. package/lib/types/client/panel/DirectorView.d.ts +7 -0
  25. package/lib/types/client/panel/GlobalAssetLibraryView.d.ts +4 -0
  26. package/lib/types/client/panel/IdeaInspirationView.d.ts +6 -0
  27. package/lib/types/client/panel/ImportModal.d.ts +7 -0
  28. package/lib/types/client/panel/KnowledgeBaseView.d.ts +4 -0
  29. package/lib/types/client/panel/LiveFeedLog.d.ts +13 -0
  30. package/lib/types/client/panel/MarketRadarView.d.ts +5 -0
  31. package/lib/types/client/panel/ModelManager.d.ts +14 -0
  32. package/lib/types/client/panel/NovelPanel.d.ts +6 -1
  33. package/lib/types/client/panel/PageHeader.d.ts +18 -0
  34. package/lib/types/client/panel/ProgressConsole.d.ts +17 -0
  35. package/lib/types/client/panel/ProgressHomeView.d.ts +5 -0
  36. package/lib/types/client/panel/PromptSlotsView.d.ts +11 -0
  37. package/lib/types/client/panel/ReasoningSection.d.ts +10 -0
  38. package/lib/types/client/panel/SettingsView.d.ts +19 -0
  39. package/lib/types/client/panel/ShelfView.d.ts +12 -1
  40. package/lib/types/client/panel/SubPage.d.ts +27 -0
  41. package/lib/types/client/panel/TensionView.d.ts +18 -0
  42. package/lib/types/client/panel/TimelineView.d.ts +18 -0
  43. package/lib/types/client/panel/helpers.d.ts +5 -3
  44. package/lib/types/client/panel/toast.d.ts +2 -0
  45. package/lib/types/client/panel/views.d.ts +25 -7
  46. package/lib/types/client/run-status.d.ts +21 -0
  47. package/lib/types/client/text.d.ts +9 -0
  48. package/lib/types/docx-core.d.ts +16 -0
  49. package/lib/types/docx.d.ts +2 -3
  50. package/lib/types/engine.d.ts +186 -45
  51. package/lib/types/global-assets.d.ts +6 -0
  52. package/lib/types/index.d.ts +22 -8
  53. package/lib/types/llm-live.d.ts +75 -0
  54. package/lib/types/llm-retry.d.ts +38 -0
  55. package/lib/types/market-radar-scan.d.ts +19 -0
  56. package/lib/types/market-radar-sources.d.ts +31 -0
  57. package/lib/types/migrate-output.d.ts +25 -0
  58. package/lib/types/novel-context.d.ts +80 -0
  59. package/lib/types/prompt-slots.d.ts +57 -0
  60. package/lib/types/prompt-utils.d.ts +35 -0
  61. package/lib/types/prompting.d.ts +10 -0
  62. package/lib/types/protocol.d.ts +1362 -166
  63. package/lib/types/review-policy.d.ts +97 -0
  64. package/lib/types/revision.d.ts +76 -0
  65. package/lib/types/routes.d.ts +4 -0
  66. package/lib/types/run.d.ts +23 -1
  67. package/lib/types/snapshots.d.ts +46 -0
  68. package/lib/types/stage-contract.d.ts +60 -0
  69. package/lib/types/tension.d.ts +53 -0
  70. package/lib/types/timeline.d.ts +37 -0
  71. package/package.json +52 -28
  72. package/src/action-guard.ts +127 -0
  73. package/src/ai-scan.ts +179 -0
  74. package/src/assets.ts +1806 -520
  75. package/src/assistant.ts +1098 -770
  76. package/src/author-assets.ts +149 -0
  77. package/src/bookshelf.ts +187 -151
  78. package/src/client/api.ts +378 -21
  79. package/src/client/chip-prefs.ts +121 -0
  80. package/src/client/docx.ts +37 -69
  81. package/src/client/index.ts +3 -1
  82. package/src/client/llmLive.ts +111 -0
  83. package/src/client/locales.ts +28 -253
  84. package/src/client/mount.tsx +1 -1
  85. package/src/client/panel/AdaptModeView.tsx +535 -0
  86. package/src/client/panel/AiTag.tsx +7 -0
  87. package/src/client/panel/AssetsTab.tsx +448 -458
  88. package/src/client/panel/AssistantTab.tsx +355 -301
  89. package/src/client/panel/AuthorAssetsView.tsx +206 -0
  90. package/src/client/panel/AuthorHome.tsx +309 -0
  91. package/src/client/panel/BookAnalysisView.tsx +63 -0
  92. package/src/client/panel/CmdPalette.tsx +102 -0
  93. package/src/client/panel/CreateBookView.tsx +366 -348
  94. package/src/client/panel/DirectorView.tsx +200 -0
  95. package/src/client/panel/GlobalAssetLibraryView.tsx +168 -0
  96. package/src/client/panel/IdeaInspirationView.tsx +76 -0
  97. package/src/client/panel/ImportModal.tsx +303 -0
  98. package/src/client/panel/KnowledgeBaseView.tsx +125 -0
  99. package/src/client/panel/LiveFeedLog.tsx +204 -0
  100. package/src/client/panel/MarketRadarView.tsx +377 -0
  101. package/src/client/panel/ModelManager.tsx +205 -0
  102. package/src/client/panel/NovelPanel.tsx +3135 -2651
  103. package/src/client/panel/PageHeader.tsx +37 -0
  104. package/src/client/panel/ProgressConsole.tsx +63 -0
  105. package/src/client/panel/PromptSlotsView.tsx +97 -0
  106. package/src/client/panel/ReaderView.tsx +306 -306
  107. package/src/client/panel/ReasoningSection.tsx +31 -0
  108. package/src/client/panel/RunPanel.tsx +64 -32
  109. package/src/client/panel/SettingsView.tsx +354 -0
  110. package/src/client/panel/ShelfView.tsx +266 -236
  111. package/src/client/panel/SubPage.tsx +42 -0
  112. package/src/client/panel/TensionView.tsx +212 -0
  113. package/src/client/panel/TimelineView.tsx +208 -0
  114. package/src/client/panel/WorldTab.tsx +202 -202
  115. package/src/client/panel/helpers.ts +7 -5
  116. package/src/client/panel/panel.module.css +4481 -1655
  117. package/src/client/panel/toast.ts +51 -0
  118. package/src/client/panel/views.tsx +401 -314
  119. package/src/client/run-status.ts +171 -0
  120. package/src/client/sidebar-entry.ts +1 -1
  121. package/src/client/text.ts +28 -0
  122. package/src/docx-core.ts +53 -0
  123. package/src/docx.ts +44 -83
  124. package/src/engine.ts +4672 -2943
  125. package/src/global-assets.ts +59 -0
  126. package/src/index.ts +89 -33
  127. package/src/llm-live.ts +260 -0
  128. package/src/llm-retry.ts +77 -0
  129. package/src/market-radar-scan.ts +41 -0
  130. package/src/market-radar-sources.ts +173 -0
  131. package/src/migrate-output.ts +76 -0
  132. package/src/novel-context.ts +286 -0
  133. package/src/prompt-slots.ts +125 -0
  134. package/src/prompting.ts +26 -0
  135. package/src/protocol.ts +1388 -158
  136. package/src/review-policy.ts +161 -0
  137. package/src/revision.ts +283 -0
  138. package/src/routes.ts +1934 -434
  139. package/src/run.ts +118 -21
  140. package/src/snapshots.ts +174 -0
  141. package/src/stage-contract.ts +189 -0
  142. package/src/tension.ts +251 -0
  143. package/src/timeline.ts +159 -0
  144. package/lib/client.js.map +0 -1
  145. package/lib/index.js.map +0 -1
  146. package/lib/types/comic-presets.d.ts +0 -12
  147. package/scripts/assistant-frames.txt +0 -8
  148. package/scripts/diagnose-adapter.mts +0 -81
  149. package/scripts/diagnose-assistant.mjs +0 -58
  150. package/scripts/diagnose-bible.mjs +0 -72
  151. package/scripts/diagnose-harness.mts +0 -75
  152. package/scripts/novel-forge-restart.log +0 -10
  153. package/scripts/novel-forge-web.stderr.log +0 -2
  154. package/scripts/novel-forge-web.stdout.log +0 -1
  155. package/scripts/outline-sample.txt +0 -449
  156. package/scripts/probe.mjs +0 -6
  157. package/scripts/restart-web.ps1 +0 -88
  158. package/scripts/smoke.mts +0 -72
  159. package/scripts/upload-github.ps1 +0 -51
  160. package/src/client/panel/BookshelfBar.tsx +0 -170
  161. package/src/comic-presets.ts +0 -67
@@ -1,9 +1,15 @@
1
- /**
2
- * 纯展示组件(views):所有状态与事件处理器都留在 NovelPanel,
3
- * 这里只接收 props 渲染。便于单独维护与复用。
4
- */
5
1
  import type { ReactElement } from 'react';
6
2
  import type { AuditIssue, Plotline, PlotlineHealthReport, PlotlinePlan, RoleRecord, RoleStatusCard } from '../../protocol.ts';
3
+ /** 滑轨胶囊导航(B5-5):active 高亮为滑动指示块,随选中项平移。 */
4
+ export declare function SlideNav<T extends string>(props: {
5
+ ariaLabel: string;
6
+ items: ReadonlyArray<{
7
+ id: T;
8
+ label: ReactElement | string;
9
+ }>;
10
+ active: T;
11
+ onSelect: (id: T) => void;
12
+ }): JSX.Element;
7
13
  /** 统计格:状态摘要条 / 资产健康通用。 */
8
14
  export declare function StatCell(props: {
9
15
  label: string;
@@ -54,24 +60,36 @@ export declare function RoleCard(props: {
54
60
  onEdit: () => void;
55
61
  onRemove: () => void;
56
62
  }): ReactElement;
57
- /** 🩺 剧情健康检查报告面板。 */
63
+ /** 剧情健康检查报告面板。 */
58
64
  export declare function PlotlineHealthPanel(props: {
59
65
  report: PlotlineHealthReport;
60
66
  disabled: boolean;
61
67
  onPlan: () => void;
62
68
  onClose: () => void;
63
69
  }): ReactElement;
64
- /** ✨ AI 剧情方案面板。 */
70
+ /** 剧情方案面板。 */
65
71
  export declare function PlotlinePlanPanel(props: {
66
72
  plan: PlotlinePlan;
67
73
  disabled: boolean;
68
74
  onAdopt: (suggestion: Plotline) => void;
69
75
  onClose: () => void;
70
76
  }): ReactElement;
71
- /** ✨ AI 建议剧情线面板。 */
77
+ /** 建议剧情线面板。 */
72
78
  export declare function PlotlineSuggestionPanel(props: {
73
79
  suggestions: Plotline[];
74
80
  disabled: boolean;
75
81
  onAdopt: (suggestion: Plotline) => void;
76
82
  onClose: () => void;
77
83
  }): ReactElement;
84
+ /** 空状态基元:图标 + 标题 + 可选提示。compact 变体用于列表/卡片内嵌场景。 */
85
+ export declare function EmptyState(props: {
86
+ icon?: string;
87
+ title: string;
88
+ hint?: string;
89
+ compact?: boolean;
90
+ }): ReactElement;
91
+ /** 骨架屏基元:rows 条微光占位行;widths 指定各行宽度档位,循环取用。 */
92
+ export declare function SkeletonLines(props: {
93
+ rows?: number;
94
+ widths?: Array<'full' | 'wide' | 'half'>;
95
+ }): ReactElement;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * 运行状态芯片:固定在页面顶部的「小说工坊生成状态」小徽标。
3
+ * 轮询 /run/status,实时显示 生成中/暂停/完成/停止/出错 + 当前章号;
4
+ * 点击可打开/关闭小说工坊面板。独立 DOM 注入,不触碰主面板逻辑,挂载失败静默降级。
5
+ * 显示偏好(关 / 仅生成中暂停时 / 总是)存在 localStorage,见 chip-prefs.ts:
6
+ * 书架页「设置 · 全局 → 外观与主题」里有对应下拉,选择即时生效(跨标签页也同步)。
7
+ *
8
+ * P0 重写说明(原实现的三个问题):
9
+ * 1. 整块背景染状态色 + 固定白字 → 浅色主题下「白字配亮底」对比度崩坏。
10
+ * 现在改为「玻璃底 + 主题前景色文字 + 左侧状态色点」,状态只由色点承载,
11
+ * 文字永远跟随主题,任何深浅色下都可读。
12
+ * 2. 样式写死在 JS 的 inline style 里,切主题纹丝不动。
13
+ * 现在改用 CSS Module class,配色走 --nf-chip-* 令牌,
14
+ * 由 body[data-ds-dark-theme] 跟随宿主深浅色。
15
+ * 3. role="button" 却没有 tabindex、没有键盘处理,键盘用户完全用不了。
16
+ * 现在补 tabindex + Enter/Space 激活 + aria-label/live。
17
+ */
18
+ import type { NovelApi } from './api.ts';
19
+ import type { PanelController } from './panel/controller.ts';
20
+ /** 生成一个固定顶部的状态芯片并开始轮询。返回 dispose。 */
21
+ export declare function mountRunStatus(controller: PanelController, api: NovelApi): () => void;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 客户端文件文本智能解码:兼容 UTF-8 BOM / UTF-16 / UTF-8 / GB18030(GBK)。
3
+ * 浏览器 File.text() 固定按 UTF-8 解码,GBK 中文网文 txt 会乱码,这里兜底。
4
+ */
5
+ /**
6
+ * 读取一个 File 的文本,按编码探测兜底解码。
7
+ * @returns 解码后的文本。
8
+ */
9
+ export declare function readFileTextSmart(file: File): Promise<string>;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * docx 文本提取共享核心:服务端(src/docx.ts)与浏览器端(src/client/docx.ts)
3
+ * 共用 XML → 纯文本的解析逻辑,避免两份实现漂移。
4
+ * zip 解压入口因运行时不同(fflate vs fflate/browser,浏览器端必须走 browser
5
+ * 入口以避开 node:module 的 require 探测)留在两端各自处理。
6
+ */
7
+ /** 解码 docx 正文实际会用到的少量 XML 实体。 */
8
+ export declare function decodeEntities(text: string): string;
9
+ /**
10
+ * 从 word/document.xml 的 XML 字符串提取纯文本:每个 <w:p> 段落一行,
11
+ * <w:tab>/<w:br> 保留为空白符;表格与嵌套结构按文档顺序拍平
12
+ * (它们的段落同样是 <w:p>)。
13
+ * @param xml - document.xml 解码后的字符串。
14
+ * @returns 正文文本(空文本时抛错)。
15
+ */
16
+ export declare function extractDocxTextFromXml(xml: string): string;
@@ -2,11 +2,10 @@
2
2
  * docx outline extraction: a .docx is a zip whose word/document.xml holds the
3
3
  * body text in <w:t> runs inside <w:p> paragraphs. We unzip with fflate and
4
4
  * walk the XML with a tiny tokenizer — no heavyweight XML/DOM dependency.
5
+ * XML 解析共享核心在 ./docx-core.ts(与浏览器端同源)。
5
6
  */
6
7
  /**
7
- * Extract plain text from a docx buffer: one line per <w:p> paragraph, with
8
- * <w:tab>/<w:br> preserved as whitespace. Tables and nested structures are
9
- * flattened in document order (their paragraphs are just <w:p> too).
8
+ * Extract plain text from a docx buffer: one line per <w:p> paragraph.
10
9
  * @param buffer - the raw .docx bytes.
11
10
  * @returns the body text.
12
11
  */
@@ -7,12 +7,12 @@
7
7
  */
8
8
  /**
9
9
  * 内容合规红线(平台硬性要求):所有书籍、所有章节无条件生效,
10
- * 优先级高于单书大纲/圣经中的任何设定与作者自定义红线。
10
+ * 优先级高于单书大纲/道藏中的任何设定与作者自定义红线。
11
11
  * 注入点:章节生成系统提示 + 审稿系统提示(命中即 high)。
12
12
  */
13
13
  export declare const COMPLIANCE_REDLINES: ReadonlyArray<string>;
14
14
  import type { Context } from '@deepseek-ai/cordis';
15
- import type { AuditIssue, AuthorReview, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, SceneCard, StoryBible, Volume, WorldState } from './protocol.ts';
15
+ import type { AdaptationDimension, AdaptAnalyzeResponse, AdaptationMapping, AdaptationRules, AdaptProposeResponse, AuditIssue, AuthorReview, ExportScope, TimelineEvent, TimelineIssue, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, StoryBible, AddModelRequest, AddModelResponse, LlmModelsResponse, LlmVendorsResponse, LlmProvidersResponse, RemoveProviderRequest, RemoveProviderResponse, LlmTestResponse, Volume, WorldState, AdaptMaterializeRequest, AdaptMaterializeResponse, AdaptMaterializeSaveRequest, AdaptMaterializeSaveResponse, MarketRadarRequest, MarketRadarResult, MarketRadarSignal, MarketRadarBriefRequest, ProductionFoundation, MarketCreativeBrief, BookAnalysisRequest, BookAnalysisResult, IdeaInspirationRequest, IdeaInspirationResult, DirectorRequest, DirectorAdvice } from './protocol.ts';
16
16
  /** Project state file name inside the output dir. */
17
17
  export declare const PROJECT_FILE = "novel-project.json";
18
18
  /** Chapter output file name, e.g. 第001章_开篇.md */
@@ -63,45 +63,43 @@ export declare function reviewChapterText(ctx: Context, config: NovelConfig, pro
63
63
  export declare function authorReviewChapter(ctx: Context, config: NovelConfig, project: ProjectState, chapterNo: number, body: string, prevTail: string): Promise<AuthorReview>;
64
64
  /** 复盘后自动关联:把本章号写入复盘标记推进的剧情线(按名称匹配,去重)。 */
65
65
  export declare function autoLinkPlotlines(project: ProjectState, chapterNo: number, advancedLines: string[]): void;
66
- /** AI 建议剧情线:基于大纲/卷计划/已写章节/编年录,提炼候选线。 */
66
+ /** 建议剧情线:基于大纲/卷计划/已写章节/编年录,提炼候选线。 */
67
67
  export declare function suggestPlotlines(ctx: Context, config: NovelConfig, project: ProjectState): Promise<Plotline[]>;
68
- /** AI 刷新单条剧情线的进度:结合编年录与各章摘要分析该线推进到哪。 */
68
+ /** 刷新单条剧情线的进度:结合编年录与各章摘要分析该线推进到哪。 */
69
69
  export declare function refreshPlotlineProgress(ctx: Context, config: NovelConfig, project: ProjectState, line: Plotline): Promise<string>;
70
- /** ✨ AI 从全书提炼角色库:大纲 + 道藏 + 编年录 + 章节摘要 → 结构化角色清单。 */
70
+ /** AI 从全书提炼角色库:大纲 + 道藏 + 编年录 + 章节摘要 → 结构化角色清单。 */
71
71
  export declare function extractRoles(ctx: Context, config: NovelConfig, project: ProjectState): Promise<RoleRecord[]>;
72
- /** ✨ AI 从全书提炼场景库:正文/编年录 → 高频重要场景的结构化视觉锚点。 */
73
- export declare function extractScenes(ctx: Context, config: NovelConfig, project: ProjectState): Promise<SceneCard[]>;
74
- /** 动漫形象描述词(中文描述 + 英文 booru 标签 + 关键外貌标签)。 */
75
- export interface RoleVisualPrompt {
76
- zh: string;
77
- en: string;
78
- tags: string[];
79
- source: string;
80
- /** 该角色所需情绪表情清单(6-12 个,如 疲惫/麻木/压抑悲伤)。 */
81
- expressions?: string[];
82
- /** 四类精修提示词(立绘/四视图/表情/细节,一次提炼直接产出)。 */
83
- promptKit?: RoleRecord['promptKit'];
84
- }
85
- /**
86
- * 为单个角色提炼「动漫形象描述词」:扫描该角色出场的已写章节正文,
87
- * 截取含外貌描写的段落,交给 LLM 提炼中文描述 + 英文绘图标签。
88
- */
89
- export declare function extractRoleVisual(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, roleName: string): Promise<RoleVisualPrompt>;
90
- /**
91
- * 角色四类生图提示词精修包(promptKit):以锚点 + 表情清单 + 视觉规则为输入,
92
- * LLM 产出 立绘/四视图/表情×N/细节 四套 zh+en 提示词(比前端拼装更精炼)。
93
- */
94
- export declare function generateRolePromptKit(ctx: Context, config: NovelConfig, project: ProjectState, roleName: string): Promise<RoleRecord['promptKit']>;
95
- /** ✨ 从道藏/红线提炼「视觉世界观规则」:生图/生视频必须遵守的设定纠偏(如"商品=人,禁止常规超市商品")。 */
96
- export declare function extractVisualRules(ctx: Context, config: NovelConfig, project: ProjectState): Promise<string[]>;
97
- /**
98
- * 开书想法 → AI 大纲:输入一句话想法,生成 2-3 个方向不同、可直接开书的完整大纲方案。
99
- * @param count 本次生成几个(默认 3,最多 3)
100
- * @param exclude 已暂留方案的剧情方向/卖点摘要(换批时避开,防止重复)
101
- */
72
+ /** AI 从全书提炼场景库:正文/编年录 → 高频重要场景的结构化视觉锚点。 */
73
+ export declare function splitBookText(raw: string): Array<{
74
+ no: number;
75
+ title: string;
76
+ body: string;
77
+ }>;
78
+ /** 拆章预览(不落盘):章节编号/标题/字数 + 跳过清单。 */
79
+ export declare function previewBookText(raw: string): {
80
+ chapters: Array<{
81
+ no: number;
82
+ title: string;
83
+ chars: number;
84
+ }>;
85
+ skipped: string[];
86
+ };
87
+ /** 从全本文本导入(浏览器上传 / 服务器文件共用):建项目、写章节文件、保存。 */
88
+ export declare function importBookTextFromText(raw: string, outputDir: string, bookName: string): {
89
+ bookName: string;
90
+ chapters: number;
91
+ skipped: string[];
92
+ };
93
+ /** 从 txt/md 全本文件导入:编码自适应读取后拆章建项目,status=written(待审稿)。 */
94
+ export declare function importBookText(filePath: string, outputDir: string): {
95
+ bookName: string;
96
+ chapters: number;
97
+ skipped: string[];
98
+ };
99
+ /** 保证风格词块已内嵌(LLM 偶发漏嵌时兜底):zh 段首前缀,en 末尾追加。 */
102
100
  export declare function suggestOutlines(ctx: Context, config: NovelConfig, idea: string, count?: number, exclude?: string[]): Promise<OutlineCandidate[]>;
103
101
  /** 拆书分析:对已写章节做结构/人物/文风/卖点四维体检。
104
- * 两阶段管道(借鉴 AI-Novel-Writing-Assistant):
102
+ * 两阶段管道(先抽取候选、再落库):
105
103
  * ① 源片段笔记:每章抽取结构化笔记(剧情/人物/设定/写法/卖点/短板信号)
106
104
  * ② 分节分析:按维度各跑一次 LLM,输出可读分析稿 + 结构化数据 + 证据链。
107
105
  * @param scope 'recent'(默认最近20章) | 'volume:N' | 'all'
@@ -109,10 +107,24 @@ export declare function suggestOutlines(ctx: Context, config: NovelConfig, idea:
109
107
  * @param budgetTokens token 预算上限(超过即截断章节取样)。
110
108
  */
111
109
  export declare function breakdownBook(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, scope?: string, preset?: 'quick' | 'standard', budgetTokens?: number): Promise<BreakdownResponse>;
112
- export declare function generateRoleReferenceImage(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, name: string, style?: string): Promise<string>;
113
- /** 🩺 剧情健康检查:基于已写章节数/各线状态/编年录,判断是否需要新线及添加时机。 */
110
+ /** 对选中的提供商/模型发一次最小真实调用(maxTokens=16),验证 Key / 端点 / 模型可用。 */
111
+ export declare function testLlmModel(ctx: Context, provider: string, model: string): Promise<LlmTestResponse>;
112
+ /** 运行时厂商目录:DSH pi-ai 可配置提供方 + 内置适配器,作为「添加模型」下拉。 */
113
+ export declare function listLlmVendors(ctx: Context): Promise<LlmVendorsResponse>;
114
+ /** 查询某个 provider 当前可用模型(添加成功后可即时刷新下拉)。 */
115
+ export declare function listLlmModels(ctx: Context, provider: string): Promise<LlmModelsResponse>;
116
+ /** 当前已注册的提供方路由列表(提供方管理卡片)。 */
117
+ export declare function listLlmProviders(ctx: Context): Promise<LlmProvidersResponse>;
118
+ /** 移除一个提供方:unset 凭据 ref + 移除 llm-pi-ai providers 路由。 */
119
+ export declare function removeLlmProvider(ctx: Context, req: RemoveProviderRequest): Promise<RemoveProviderResponse>;
120
+ /**
121
+ * 添加模型(DSH 同款体验):厂商直填 API Key,或自定义 OpenAI 兼容路由。
122
+ * 写入 DSH 凭据 refs,并(必要时)注册/更新 llm-pi-ai provider 路由。
123
+ */
124
+ export declare function registerLlmModel(ctx: Context, req: AddModelRequest): Promise<AddModelResponse>;
125
+ /** 剧情健康检查:基于已写章节数/各线状态/编年录,判断是否需要新线及添加时机。 */
114
126
  export declare function analyzePlotlineHealth(ctx: Context, config: NovelConfig, project: ProjectState): Promise<PlotlineHealthReport>;
115
- /** ✨ AI 剧情方案:基于健康检查结果设计下一阶段方向与建议新线。 */
127
+ /** 剧情方案:基于健康检查结果设计下一阶段方向与建议新线。 */
116
128
  export declare function designPlotlinePlan(ctx: Context, config: NovelConfig, project: ProjectState, health?: PlotlineHealthReport): Promise<PlotlinePlan>;
117
129
  /**
118
130
  * Stream a chapter rewrite. With `target` (a passage of the body), only that
@@ -142,6 +154,8 @@ export declare function polishChapterStream(ctx: Context, config: NovelConfig, p
142
154
  chars: number;
143
155
  draft: string;
144
156
  }, void, unknown>;
157
+ /** 统计中文字符数(汉字),与「每章字数」红线口径一致(不含标点/换行)。 */
158
+ export declare function countHanzi(text: string): number;
145
159
  /** Generate one chapter (streaming). Yields progress frames; persists when done. */
146
160
  export declare function generateChapterStream(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number): AsyncGenerator<{
147
161
  frame: 'start';
@@ -152,14 +166,75 @@ export declare function generateChapterStream(ctx: Context, config: NovelConfig,
152
166
  frame: 'done';
153
167
  file: string;
154
168
  chars: number;
169
+ warn?: string;
155
170
  }, void, unknown>;
156
171
  /** Generate a chapter summary (narrative memory). */
157
172
  export declare function summarizeChapter(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number): Promise<string>;
158
173
  /**
159
- * 摘要 + 事实抽取合并为一次 LLM 调用(省一次调用与一次正文输入,
160
- * 批量生成时整体开销约省 25%)。
161
- * @returns 摘要与新增事实条数(失败返回空,调用方 best-effort)。
174
+ * 反向推大纲:从已写章节正文反推出全书总纲(分卷 + 章节要点 + 主线/人物弧线/伏笔清单)。
175
+ * 两阶段:分批提取章节事件摘要 → 汇总生成大纲。不修改章节/设定,只返回大纲文本。
176
+ */
177
+ export declare function reverseOutlineFromChapters(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, onProgress?: (done: number, total: number, phase: string) => void): Promise<string>;
178
+ /**
179
+ * 改编模式 P0:全文分析 → 原文设定卡片 / 可改范围矩阵。
180
+ * 拆章统计 + 取样正文,让 LLM 一次输出结构化 JSON:
181
+ * { bookName, outline, dimensions: [{key,title,mutability,current,evidence,candidates,impact,risk}] }。
182
+ */
183
+ export declare function analyzeAdaptation(ctx: Context, config: NovelConfig, text: string): Promise<AdaptAnalyzeResponse>;
184
+ /** 改编方案:由用户勾选的维度与新值,生成 LLM 映射表/规则/影响清单。 */
185
+ export declare function proposeAdaptation(ctx: Context, config: NovelConfig, _text: string, selections: Array<{
186
+ key: string;
187
+ title: string;
188
+ current: string;
189
+ target: string;
190
+ mutability: string;
191
+ }>, dimensions?: AdaptationDimension[]): Promise<AdaptProposeResponse>;
192
+ /** 剧本术语替换执行:按映射表做精确替换并统计命中。 */
193
+ export declare function applyAdaptationReplacements(text: string, mappings: AdaptationMapping[]): {
194
+ adaptedText: string;
195
+ hits: Array<{
196
+ source: string;
197
+ target: string;
198
+ count: number;
199
+ }>;
200
+ };
201
+ /** 改编模式 rewrite:逐章 LLM 重写(结构性改写,不只是换词)。
202
+ * @returns 改写后的全文 + 逐章结果 + 保留原章的章号。 */
203
+ export declare function rewriteAdaptationBook(ctx: Context, config: NovelConfig, text: string, mappings: AdaptationMapping[], rules?: AdaptationRules, options?: {
204
+ maxChapters?: number;
205
+ startNo?: number;
206
+ endNo?: number;
207
+ onProgress?: (info: {
208
+ completed: number;
209
+ total: number;
210
+ no: number;
211
+ title: string;
212
+ }) => void;
213
+ }): Promise<{
214
+ adaptedText: string;
215
+ rewritten: Array<{
216
+ no: number;
217
+ title: string;
218
+ chars: number;
219
+ }>;
220
+ skipped: number[];
221
+ hits: Array<{
222
+ source: string;
223
+ target: string;
224
+ count: number;
225
+ }>;
226
+ }>;
227
+ /**
228
+ * 改编模式 P3:从源全文 + 用户编辑后的改编方案,提炼新书资料并保存为「待写新书」。
229
+ * 流程:源文导入临时项目 → 复用 extractBible/extractRoles/extractWorld 提炼 →
230
+ * 按映射表把术语/人名/势力映射到新书命名层 → planVolumes/planChapters 生成待写计划 → 保存。
231
+ * @returns 提炼后的新书资料(不含书架 book,由路由负责登记书架)。
162
232
  */
233
+ export declare function materializeAdaptedBook(ctx: Context, config: NovelConfig, args: Omit<AdaptMaterializeRequest, 'outputDir'> & {
234
+ outputDir: string;
235
+ }): Promise<Omit<AdaptMaterializeResponse, 'book'>>;
236
+ /** 把预览/微调后的新书资料写入输出目录并返回摘要(书架登记由路由负责)。 */
237
+ export declare function saveMaterializedBook(outDir: string, bookName: string, data: Omit<AdaptMaterializeSaveRequest, 'bookName' | 'outputDir'>): Omit<AdaptMaterializeSaveResponse, 'book'>;
163
238
  export declare function summarizeAndExtractFacts(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number): Promise<{
164
239
  summary: string;
165
240
  factCount: number;
@@ -178,7 +253,7 @@ export declare function markForeshadowPlanted(project: ProjectState, outputDir:
178
253
  export declare function extractFacts(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number): Promise<number>;
179
254
  /** 全书一致性质检:LLM 分批扫描已生成章节 + 设定 + 事实库,聚合矛盾清单。 */
180
255
  export declare function auditBook(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, onProgress?: (completedBatches: number, totalBatches: number) => void): Promise<AuditIssue[]>;
181
- /** 小说简介:AI 生成或按已写开头补全(面向读者的作品门面)。 */
256
+ /** 小说简介:生成或按已写开头补全(面向读者的作品门面)。 */
182
257
  export declare function generateBlurb(ctx: Context, config: NovelConfig, project: ProjectState, partial?: string): Promise<string>;
183
258
  /**
184
259
  * 组装全书上下文包(AI 助手 book_overview 工具)。
@@ -188,7 +263,7 @@ export declare function generateBlurb(ctx: Context, config: NovelConfig, project
188
263
  export declare function bookOverview(project: ProjectState, scope?: 'recent' | 'full' | number): string;
189
264
  /** 一条影响分析结果(改动波及处)。 */
190
265
  export interface ImpactItem {
191
- /** 位置:章节号 / 大纲 / 设定圣经 / 大世界 / 事实库 / 简介。 */
266
+ /** 位置:章节号 / 大纲 / 道藏 / 大世界 / 事实库 / 简介。 */
192
267
  location: string;
193
268
  /** 原文片段(定位用)。 */
194
269
  quote: string;
@@ -204,7 +279,7 @@ export interface ImpactItem {
204
279
  export declare function analyzeImpact(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, change: string): Promise<ImpactItem[]>;
205
280
  /** 把大世界结构化数据渲染成提示词块(境界体系按顺序强约束)。 */
206
281
  export declare function renderWorld(world: WorldState | undefined): string;
207
- /** AI 提炼大世界:从大纲 + 设定圣经生成结构化境界体系/区域/势力。 */
282
+ /** 提炼大世界:从大纲 + 道藏生成结构化境界体系/区域/势力。 */
208
283
  export declare function extractWorld(ctx: Context, config: NovelConfig, project: ProjectState): Promise<WorldState>;
209
284
  /**
210
285
  * 事实库回填:对历史已生成章节批量抽取事实(无事实记录的旧章节)。
@@ -227,10 +302,76 @@ export declare function extractStyleAsset(ctx: Context, config: NovelConfig, sam
227
302
  dialogueRules: string[];
228
303
  descriptionRules: string[];
229
304
  boundaries: string[];
305
+ preset?: 'imitate' | 'balanced' | 'transfer';
306
+ fingerprintRisk?: 'low' | 'medium' | 'high';
307
+ writingGuidance?: string[];
308
+ forbiddenEntities?: string[];
309
+ }>;
310
+ /** 分层提取写作公式(basic/standard/deep)。 */
311
+ export declare function extractStyleFormula(ctx: Context, config: NovelConfig, sampleText: string, depth: 'basic' | 'standard' | 'deep'): Promise<{
312
+ name: string;
313
+ focusAreas: string[];
314
+ formula: string;
315
+ applyGuidance: string;
230
316
  }>;
317
+ /** 题材雷达:输入平台/题材/榜单文本 → 信号 + 生产底座 + 开书创意。 */
318
+ export declare function runMarketRadar(ctx: Context, config: NovelConfig, req: MarketRadarRequest): Promise<MarketRadarResult>;
319
+ /** 开书创意简报:用选中的市场信号 + 影响模式生成可执行 constraint / creative seed。 */
320
+ export declare function runMarketCreativeBrief(ctx: Context, config: NovelConfig, req: MarketRadarBriefRequest): Promise<MarketCreativeBrief>;
321
+ /** 书分析/拆书:输入一本书/章节文本 → 卖点/结构/可借鉴点/风险。 */
322
+ export declare function runBookAnalysis(ctx: Context, config: NovelConfig, req: BookAnalysisRequest): Promise<BookAnalysisResult>;
323
+ /** 创意灵感:一句话/题材 → 多方向开书灵感。 */
324
+ export declare function runIdeaInspiration(ctx: Context, config: NovelConfig, req: IdeaInspirationRequest): Promise<IdeaInspirationResult>;
325
+ /** 雷达→灵感:基于市场信号/生产底座/创意简报,生成贴合市场的开书灵感。 */
326
+ export declare function runMarketIdeaInspiration(ctx: Context, config: NovelConfig, req: {
327
+ signals?: MarketRadarSignal[];
328
+ foundation?: ProductionFoundation;
329
+ brief?: MarketCreativeBrief;
330
+ count?: number;
331
+ }): Promise<IdeaInspirationResult>;
332
+ /** 自动导演编排建议:基于全书上下文,给出下一卷/阶段编排 + 修复再平衡。 */
333
+ export declare function runDirectorAdvice(ctx: Context, config: NovelConfig, project: ProjectState, req: DirectorRequest): Promise<DirectorAdvice>;
334
+ /**
335
+ * 从一章正文抽取时间线事件(时间点 / 地点 / 参与角色 / 事件)。
336
+ *
337
+ * 会**替换**该章已有事件(重复抽取幂等);抽取结果按 order 递增排序后写回项目。
338
+ * 失败时抛错,由调用方决定是否降级(自动流程用 try/catch 包住,不阻断出章)。
339
+ *
340
+ * @param ctx 宿主上下文。
341
+ * @param config 运行时配置。
342
+ * @param project 项目状态(就地更新 `project.timeline`)。
343
+ * @param outputDir 书目录。
344
+ * @param chapterNo 章节号。
345
+ * @returns 本次抽取到的事件(已写入 project)。
346
+ */
347
+ export declare function extractTimelineForChapter(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number): Promise<TimelineEvent[]>;
348
+ /**
349
+ * AI 复核时间线:在规则初筛之外,让模型找规则抓不到的时序问题
350
+ * (年龄/季节错位、回忆与当前时间混淆、跨章跨度不合理等)。
351
+ *
352
+ * @param ctx 宿主上下文。
353
+ * @param config 运行时配置。
354
+ * @param project 项目状态。
355
+ * @returns 模型给出的问题清单(与规则初筛结果合并由调用方决定)。
356
+ */
357
+ export declare function reviewTimeline(ctx: Context, config: NovelConfig, project: ProjectState): Promise<TimelineIssue[]>;
231
358
  /** Export the whole book as one txt/md file. */
232
359
  export declare function exportBook(outputDir: string, project: ProjectState, format: 'txt' | 'md'): {
233
360
  file: string;
234
361
  chars: number;
235
362
  chapters: number;
236
363
  };
364
+ /**
365
+ * 按范围导出:整本正文 / 设定 / 规划 / 角色 / 质检记录 / 项目 JSON。
366
+ *
367
+ * @param outputDir 书目录。
368
+ * @param project 项目状态。
369
+ * @param scope 导出范围。
370
+ * @param format txt / md / json(json 只对 `project` 范围有独立含义,其余按 md 落盘)。
371
+ * @returns 文件名、字符数与章节数。
372
+ */
373
+ export declare function exportScope(outputDir: string, project: ProjectState, scope: ExportScope, format: 'txt' | 'md' | 'json'): {
374
+ file: string;
375
+ chars: number;
376
+ chapters: number;
377
+ };
@@ -0,0 +1,6 @@
1
+ import type { GenreNode, ProgressionMode } from './protocol.ts';
2
+ export declare function globalGenreLibrary(): GenreNode[];
3
+ export declare function globalProgressionLibrary(): ProgressionMode[];
4
+ /** 新增/已有则跳过;返回 true=新增。 */
5
+ export declare function addGlobalGenre(g: GenreNode): boolean;
6
+ export declare function addGlobalMode(m: ProgressionMode): boolean;
@@ -18,7 +18,7 @@ export declare const inject: string[];
18
18
  * settings surface edits. Spelled here rather than imported: the browser half
19
19
  * spells the same value and must not depend on a Host package.
20
20
  */
21
- export declare const NOVEL_SETTINGS_NAMESPACE: import("@deepseek-ai/dsh-settings").SettingsNamespace;
21
+ export declare const NOVEL_SETTINGS_NAMESPACE: "dsh-novel-forge";
22
22
  /** Plugin config, validated by the same-named schemastery schema. */
23
23
  export interface Config {
24
24
  /** When true (default), a system-prompt section announces the plugin to every agent. */
@@ -33,8 +33,20 @@ export interface Config {
33
33
  provider?: string;
34
34
  /** LLM model id. */
35
35
  model?: string;
36
+ /** 任务级模型路由:正文生成模型(留空则跟随 model)。 */
37
+ generateModel?: string;
38
+ /** 任务级模型路由:审稿模型(留空则跟随 model)。 */
39
+ reviewModel?: string;
40
+ /** 任务级模型路由:质检模型(留空则跟随 model)。 */
41
+ auditModel?: string;
42
+ /** 备用模型:主模型失败时自动切换重试一次(留空 = 不切换)。 */
43
+ fallbackModel?: string;
44
+ /** 出章后自动抽取故事时间线(默认开)。 */
45
+ autoTimeline?: boolean;
36
46
  /** LLM reasoning effort (off/low/high/max). */
37
47
  reasoningEffort?: 'off' | 'low' | 'high' | 'max';
48
+ /** 分析类任务(提炼/拆书/反推大纲等)的推理档位;默认 low。 */
49
+ analysisReasoning?: 'off' | 'low' | 'high' | 'max';
38
50
  /** Target characters per chapter. */
39
51
  chapterChars?: number;
40
52
  /** Max output tokens per chapter call. */
@@ -47,16 +59,18 @@ export interface Config {
47
59
  autoAuthorReview?: boolean;
48
60
  /** 修订/润色产出草稿后自动附带一次 AI 审查(默认开,可在设置页关闭省 token)。 */
49
61
  autoReviewAfterRevise?: boolean;
50
- /** 豆包/Seedream 生图 API Key。 */
51
- imageApiKey?: string;
52
- /** 豆包/Seedream 生图模型 ID。 */
53
- imageApiModel?: string;
54
- /** 是否启用豆包生图(默认关)。 */
55
- imageApiEnabled?: boolean;
62
+ /** 自定义背景图(URL / dataURL / 服务端路径引用)。 */
63
+ themeBackground?: string;
64
+ /** 自定义背景遮罩/模糊强度 0-80。 */
65
+ themeBackgroundBlur?: number;
66
+ /** 玻璃透明度 0-100(100=当前原样)。 */
67
+ themeOpacity?: number;
68
+ /** 是否启用改编模式(默认关闭)。 */
69
+ enableAdaptMode?: boolean;
56
70
  }
57
71
  export declare const Config: z<Config>;
58
72
  /** Model-facing announcement: plugin presence, capabilities, and limits. */
59
- export declare const NOVEL_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-novel-forge \u63D2\u4EF6\uFF08AI \u7F16\u8BD1\u5C0F\u8BF4\u5DE5\u4F5C\u53F0\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300C\u5C0F\u8BF4\u5DE5\u574A\u300D\u5165\u53E3\u3002\u80FD\u529B\uFF1A\u8BFB\u53D6 docx \u5927\u7EB2\uFF08\u9ED8\u8BA4 D:\\ryan work\\harness\\novels\\\u5F52\u589F\u7389\u4E3B \u5927\u7EB2\uFF09\u6216\u7C98\u8D34\u5927\u7EB2\u6587\u672C\uFF1B\u7528 LLM \u63D0\u70BC\u9053\u85CF\uFF08\u4EBA\u8BBE/\u4E16\u754C\u89C2/\u91D1\u624B\u6307\u89C4\u5219/\u5199\u4F5C\u7EA2\u7EBF\uFF0C\u5373\u8BBE\u5B9A\u5723\u7ECF\uFF09\uFF1B\u751F\u6210\u5377\u8BA1\u5212\u4E0E\u7AE0\u8282\u8BA1\u5212\uFF1B\u9010\u7AE0\u8C03\u7528 LLM \u751F\u6210 3000-4000 \u5B57\u6B63\u6587\u5E76\u4FDD\u5B58\u4E3A Markdown\uFF08\u9ED8\u8BA4\u8F93\u51FA\u5230 D:\\ryan work\\harness\\novels\\\u5F52\u589F\u7389\u4E3B\uFF09\uFF1B\u6BCF\u7AE0\u81EA\u52A8\u751F\u6210\u6458\u8981\uFF08\u53D9\u4E8B\u8BB0\u5FC6\uFF09\u3001\u81EA\u52A8 AI \u5BA1\u7A3F\uFF08\u4EBA\u8BBE/\u8BBE\u5B9A/\u7EA2\u7EBF/\u6587\u7B14/\u723D\u70B9/\u903B\u8F91\uFF09\uFF0C\u652F\u6301\u6309\u5BA1\u7A3F\u610F\u89C1\u91CD\u5199\u3001\u53BB AI \u5473\u6DA6\u8272\u3001\u6697\u7EBF\uFF08\u4F0F\u7B14\uFF09\u7BA1\u7406\u3001\u6279\u91CF\u8FDE\u5199\u4E0E\u5168\u672C\u5BFC\u51FA\uFF08txt/md\uFF09\u3002\u9650\u5236\uFF1A\u751F\u6210\u6D88\u8017 LLM API \u989D\u5EA6\uFF1B\u8F93\u51FA\u76EE\u5F55\u4E0E\u6A21\u578B\u53EF\u5728\u63D2\u4EF6\u8BBE\u7F6E\u4E2D\u4FEE\u6539\uFF1B\u7AE0\u8282\u6B63\u6587\u8D28\u91CF\u53D6\u51B3\u4E8E\u5927\u7EB2\u5B8C\u6574\u5EA6\u3002\u7528\u6237\u63D0\u5230\u300C\u5C0F\u8BF4 / \u5927\u7EB2 / \u5199\u5C0F\u8BF4 / \u7AE0\u8282 / \u5BA1\u7A3F / \u6DA6\u8272 / \u5F52\u589F\u7389\u4E3B\u300D\u65F6\u5373\u6307\u672C\u63D2\u4EF6\uFF0C\u8BF7\u636E\u6B64\u534F\u4F5C\u3002";
73
+ export declare const NOVEL_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-novel-forge \u63D2\u4EF6\uFF08AI \u7F16\u8BD1\u5C0F\u8BF4\u5DE5\u4F5C\u53F0\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300C\u5C0F\u8BF4\u5DE5\u574A\u300D\u5165\u53E3\u3002\u80FD\u529B\uFF1A\u8BFB\u53D6 docx \u5927\u7EB2\u3001\u7C98\u8D34\u5927\u7EB2\u6587\u672C\u6216\u628A txt \u5168\u672C\u62C6\u7AE0\u5BFC\u5165\uFF1B\u7528 LLM \u63D0\u70BC\u9053\u85CF\uFF08\u4EBA\u8BBE/\u4E16\u754C\u89C2/\u91D1\u624B\u6307\u89C4\u5219/\u5199\u4F5C\u7EA2\u7EBF\uFF09\u4E0E\u5927\u4E16\u754C\uFF08\u5883\u754C/\u533A\u57DF/\u52BF\u529B\uFF09\uFF1B\u751F\u6210\u5377\u8BA1\u5212\u4E0E\u7ED3\u6784\u5316\u7AE0\u8282\u8BA1\u5212\uFF08\u672C\u7AE0\u76EE\u6807/\u5267\u60C5\u8981\u70B9/\u5FC5\u8FBE\u9879/\u4E49\u52A1\u5408\u7EA6/\u7AE0\u672B\u94A9\u5B50\uFF09\uFF1B\u9010\u7AE0\u751F\u6210 3000-4000 \u5B57\u6B63\u6587\u5E76\u4FDD\u5B58\u4E3A Markdown\uFF08\u9ED8\u8BA4\u8F93\u51FA\u5230\u7528\u6237\u4E3B\u76EE\u5F55 ~/.dsh/novels\uFF09\uFF1B\u6BCF\u7AE0\u81EA\u52A8\u751F\u6210\u6458\u8981\u4E0E\u7F16\u5E74\u5F55\u4E8B\u5B9E\u3001\u62BD\u53D6\u6545\u4E8B\u65F6\u95F4\u7EBF\uFF08\u6545\u4E8B\u5185\u65F6\u95F4/\u5730\u70B9/\u5728\u573A\u89D2\u8272/\u4E8B\u4EF6\uFF09\u3001\u81EA\u52A8 AI \u5BA1\u7A3F\uFF08\u4EBA\u8BBE/\u8BBE\u5B9A/\u7EA2\u7EBF/\u6587\u7B14/\u723D\u70B9/\u903B\u8F91/\u53CD AI/\u5448\u73B0/\u5408\u89C4\u4E5D\u7EF4\uFF09\u4E0E\u4F5C\u8005\u590D\u76D8\uFF1B\u652F\u6301\u6309\u5BA1\u7A3F\u610F\u89C1\u5408\u5E76\u4FEE\u8BA2\uFF08\u5BA1\u7A3F high + \u65F6\u95F4\u7EBF\u77DB\u76FE + \u5F20\u529B\u504F\u5DEE\u5408\u6210\u4E00\u8F6E\uFF0C\u6BCF\u7AE0\u6700\u591A\u4E24\u8F6E\u540E\u8F6C\u4EBA\u5DE5\uFF09\u3001\u53BB AI \u5473\u6DA6\u8272\u3001\u7AE0\u8282\u5386\u53F2\u7248\u672C\u56DE\u6EDA\u3001\u6697\u7EBF\uFF08\u4F0F\u7B14\uFF09\u7BA1\u7406\u3001\u5267\u60C5\u7EBF\u4E0E\u89D2\u8272\u5E93\u3001\u5F20\u529B\u66F2\u7EBF\u3001\u5168\u4E66\u4E00\u81F4\u6027\u8D28\u68C0\u4E0E\u654F\u611F\u8BCD\u626B\u63CF\u3001\u5206\u8303\u56F4\u5BFC\u51FA\u4E0E\u9879\u76EE\u5907\u4EFD\u3001\u6279\u91CF\u8FDE\u5199\uFF08\u751F\u4EA7\u5355\uFF0C\u53EF\u6682\u505C/\u7EED\u8DD1\uFF09\u3002\u9650\u5236\uFF1A\u6240\u6709 AI \u64CD\u4F5C\u6D88\u8017 LLM API \u989D\u5EA6\uFF1B\u8F93\u51FA\u76EE\u5F55\u4E0E\u6A21\u578B\u53EF\u5728\u63D2\u4EF6\u8BBE\u7F6E\u4E2D\u4FEE\u6539\uFF1B\u7AE0\u8282\u6B63\u6587\u8D28\u91CF\u53D6\u51B3\u4E8E\u5927\u7EB2\u5B8C\u6574\u5EA6\uFF1B\u5F20\u529B/\u65F6\u95F4\u7EBF\u95EE\u9898\u9ED8\u8BA4\u53EA\u662F\u5EFA\u8BAE\uFF08\u5199\u65E5\u5FD7\u4E0E\u5F85\u529E\uFF09\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u6539\u6B63\u6587\u3002\u7528\u6237\u63D0\u5230\u300C\u5C0F\u8BF4 / \u5927\u7EB2 / \u5199\u5C0F\u8BF4 / \u7AE0\u8282 / \u5BA1\u7A3F / \u6DA6\u8272\u300D\u65F6\u5373\u6307\u672C\u63D2\u4EF6\uFF0C\u8BF7\u636E\u6B64\u534F\u4F5C\u3002";
60
74
  /** Resolve a config-like value into the full runtime config. */
61
75
  export declare function resolveConfig(value: Partial<Config> | undefined): NovelConfig;
62
76
  /**
@@ -0,0 +1,75 @@
1
+ /**
2
+ * LLM 实况 feed:以「每次 LLM 调用」为粒度,把生命周期事件(开始/流式正文/阶段变化/完成)
3
+ * 推给订阅者,并提供环形缓冲供新连上的客户端重放。
4
+ *
5
+ * 同时承担三件与用量有关的事(进程内,不落盘):
6
+ * 1) **用量账本**:每次调用结束后按用途累计 token / 耗时,供 /status 展示;
7
+ * 2) **Prompt 记录**:保存每次调用实际发送的 system + user(环形,按需查询);
8
+ * 3) **首字耗时**:调用方在拿到第一个文本增量时打点,用于观察模型响应速度。
9
+ */
10
+ import type { BlockAssembler, TokenUsage } from '@deepseek-ai/dsh-llm';
11
+ import type { LlmLiveFrame, LlmPromptRecord, LlmTokenUsage, LlmUsageSummary } from './protocol.ts';
12
+ export type { LlmLiveFrame };
13
+ export type LlmLivePhase = 'requesting' | 'streaming' | 'completed' | 'failed';
14
+ type Listener = (frame: LlmLiveFrame) => void;
15
+ export declare function nextSessionId(): string;
16
+ export declare function subscribeLiveFeed(listener: Listener): () => void;
17
+ export declare function emitLive(frame: LlmLiveFrame): void;
18
+ export declare function liveLatest(): LlmLiveFrame[];
19
+ export declare function clearLiveFeed(): void;
20
+ /** 把 dsh-llm 的 usage 转成线上形状。 */
21
+ export declare function toLiveUsage(usage: TokenUsage | undefined): LlmTokenUsage | undefined;
22
+ /** 本次运行的用量汇总(按耗时降序排列用途,便于看谁最贵)。 */
23
+ export declare function liveUsage(): LlmUsageSummary;
24
+ /** 清零用量账本(面板「重新计数」用)。 */
25
+ export declare function resetLiveUsage(): void;
26
+ /** 记一条 Prompt(超长截断并标记)。 */
27
+ export declare function recordPrompt(input: {
28
+ sessionId: string;
29
+ label?: string;
30
+ model?: string;
31
+ system?: string;
32
+ user?: string;
33
+ }): LlmPromptRecord;
34
+ /** 取一条 Prompt 记录(不存在返回 undefined)。 */
35
+ export declare function livePrompt(sessionId: string): LlmPromptRecord | undefined;
36
+ /** 清空 Prompt 记录。 */
37
+ export declare function clearLivePrompts(): void;
38
+ /** 一次调用的打点句柄。 */
39
+ export interface LiveCallHandle {
40
+ sessionId: string;
41
+ startedAt: number;
42
+ /** 用途(用于 session_completed 的用量分组)。 */
43
+ label: string;
44
+ /** 实际使用的模型。 */
45
+ model: string;
46
+ /** 首字耗时(毫秒),由 markFirstToken 写入。 */
47
+ firstTokenMs?: number;
48
+ }
49
+ /**
50
+ * 开始一次 LLM 调用:发 session_started、记 Prompt、起计时。
51
+ *
52
+ * @param input 用途/模型/实际发送的 system 与 user。
53
+ * @returns 打点句柄(结束时交给 endLiveCall)。
54
+ */
55
+ export declare function beginLiveCall(input: {
56
+ label: string;
57
+ model: string;
58
+ system?: string;
59
+ user?: string;
60
+ }): LiveCallHandle;
61
+ /** 首字打点(调用方在收到第一个文本增量时调用,只记第一次)。 */
62
+ export declare function markFirstToken(handle: LiveCallHandle): void;
63
+ /**
64
+ * 结束一次 LLM 调用:从 assembler 取用量,发 session_completed(含 token/耗时/首字)。
65
+ *
66
+ * @param handle beginLiveCall 的句柄。
67
+ * @param assembler 本次调用的 BlockAssembler。
68
+ * @param extra 字符数/预览/失败信息等。
69
+ */
70
+ export declare function endLiveCall(handle: LiveCallHandle, assembler: BlockAssembler, extra?: {
71
+ chars?: number;
72
+ preview?: string;
73
+ phase?: 'completed' | 'failed';
74
+ error?: string;
75
+ }): void;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 模型失败恢复规则(纯逻辑,可离线单测)。
3
+ *
4
+ * 解决的问题:批量连写时,主模型偶发限流/超时/额度耗尽会让**整批中断**,
5
+ * 而换一个模型重试一次往往就能过去。规则:
6
+ * 1) 只对「可重试」错误切换模型——用户主动取消、参数/契约类错误不重试;
7
+ * 2) 备用模型为空或与主模型相同则不切换;
8
+ * 3) 备用模型也失败时,抛**原始错误**(保留主因,不掩盖现场);
9
+ * 4) 每次调用最多切换一次。
10
+ */
11
+ /**
12
+ * 判断一次 LLM 失败是否值得换模型重试。
13
+ *
14
+ * @param error 捕获到的异常。
15
+ * @returns 可重试时为 true。
16
+ */
17
+ export declare function isRetryableLlmError(error: unknown): boolean;
18
+ /**
19
+ * 判断是否应当切换到备用模型。
20
+ *
21
+ * @param fallbackModel 配置的备用模型(空串 = 未配置)。
22
+ * @param primaryModel 本次实际使用的主模型。
23
+ * @param error 捕获到的异常。
24
+ * @returns 应当切换时为 true。
25
+ */
26
+ export declare function shouldSwitchModel(fallbackModel: string | undefined, primaryModel: string, error: unknown): boolean;
27
+ /**
28
+ * 用主模型执行;失败且满足切换条件时,用备用模型再执行一次。
29
+ *
30
+ * @param input 主模型 / 备用模型 / 实际执行函数(接收本次要用的模型 id)。
31
+ * @returns 执行结果(备用模型成功时返回其结果)。
32
+ * @throws 主模型失败且未切换时抛原错误;切换后仍失败时同样抛**主模型的原始错误**。
33
+ */
34
+ export declare function withModelFallback<T>(input: {
35
+ model: string;
36
+ fallbackModel?: string;
37
+ onSwitch?: (from: string, to: string, error: unknown) => void;
38
+ }, run: (model: string) => Promise<T>): Promise<T>;