dsh-novel-write-engine 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1,645 @@
1
+ /**
2
+ * Public contracts of the engine. Everything else in `src/` is an implementation
3
+ * detail; adapters (tools, commands, panel, presets) may only import this file
4
+ * and the engine service.
5
+ */
6
+ /** On-disk schema version for every YAML file the engine owns. */
7
+ export declare const SCHEMA_VERSION: 1;
8
+ /** Pipeline control-flow state. This is the state machine value, not a brief id. */
9
+ export type Stage = 'idle' | 'P2' | 'P3' | 'P4' | 'P5' | 'P6' | 'ready_to_archive' | 'done';
10
+ /** Brief (task book) ids. `P7`/`P9` are transactions and never hand out a brief. */
11
+ export type BriefStage = 'P1' | 'P2' | 'P3' | 'P4' | 'P5' | 'P6' | 'P8';
12
+ /** The twelve supported commercial genres. */
13
+ export declare const GENRES: readonly ["玄幻修仙", "都市现代", "西方奇幻", "悬疑推理", "科幻", "游戏系统", "历史架空", "无限流", "轻小说", "军事", "末世流", "言情"];
14
+ export type Genre = (typeof GENRES)[number];
15
+ /** Platform style profiles. */
16
+ export declare const PLATFORMS: readonly ["fanqie", "qidian", "qimao", "jinjiang"];
17
+ export type Platform = (typeof PLATFORMS)[number];
18
+ /** Scene types used by rhythm budgets and PG-06. */
19
+ export declare const SCENE_TYPES: readonly ["战斗/动作章", "日常过渡章", "情感爆发章", "纯叙事章"];
20
+ export type SceneType = (typeof SCENE_TYPES)[number];
21
+ /** A violation is blocking unless the rule pack declares it advisory. */
22
+ export type Severity = 'blocker' | 'advisory';
23
+ /** All twenty-two service operations. */
24
+ export declare const OPERATIONS: readonly ["initProject", "archiveChapter", "amend", "rollback", "submitWorldPlan", "submitChapterPlan", "submitDraft", "submitReview", "submitRevision", "runCheck", "getStatus", "getBrief", "countWords", "queryState", "diff", "auditHistory", "advise", "export", "panelSummary", "panelChapters", "panelGates", "stats"];
25
+ export type Operation = (typeof OPERATIONS)[number];
26
+ /** Structured, non-fatal note attached to a successful result. */
27
+ export interface Warning {
28
+ code: string;
29
+ message: string;
30
+ }
31
+ /** One reason a gate refused progress. */
32
+ export interface BlockingItem {
33
+ code: string;
34
+ scope?: string;
35
+ detail: string;
36
+ hint?: string;
37
+ }
38
+ /** Uniform success envelope of every service operation. */
39
+ export interface OpResult<T> {
40
+ data: T;
41
+ revision: number;
42
+ warnings: Warning[];
43
+ /** True when an idempotent retry was answered from the replay cache. */
44
+ replayed?: boolean;
45
+ }
46
+ /** A single reading-proof reference: the quoted text, its paragraph and chapter. */
47
+ export interface ReadingProofReference {
48
+ chapter?: number;
49
+ paragraph: number;
50
+ quote: string;
51
+ }
52
+ /** Three references proving the model read the chapter it claims to review. */
53
+ export interface ReadingProof {
54
+ opening: ReadingProofReference;
55
+ middle: ReadingProofReference;
56
+ ending: ReadingProofReference;
57
+ }
58
+ /** Error families map one-to-one to the three repair actions a caller can take. */
59
+ export declare const ERROR_CODES: {
60
+ readonly PROJECT_NOT_FOUND: "ENGINE_PROJECT_NOT_FOUND";
61
+ readonly PROJECT_EXISTS: "ENGINE_PROJECT_EXISTS";
62
+ readonly STAGE_ORDER: "ENGINE_STAGE_ORDER";
63
+ readonly GATE_BLOCKED: "ENGINE_GATE_BLOCKED";
64
+ readonly READING_PROOF_INVALID: "ENGINE_READING_PROOF_INVALID";
65
+ readonly SCORE_MISMATCH: "ENGINE_SCORE_MISMATCH";
66
+ readonly DELTA_UNVERIFIED: "ENGINE_DELTA_UNVERIFIED";
67
+ readonly RESOURCE_NEGATIVE_BALANCE: "ENGINE_RESOURCE_NEGATIVE_BALANCE";
68
+ readonly SCHEMA_TOO_NEW: "ENGINE_SCHEMA_TOO_NEW";
69
+ readonly SCHEMA_INVALID: "ENGINE_SCHEMA_INVALID";
70
+ readonly PATH_ESCAPE: "ENGINE_PATH_ESCAPE";
71
+ readonly LOCK_TIMEOUT: "ENGINE_LOCK_TIMEOUT";
72
+ readonly TXN_ABORTED: "ENGINE_TXN_ABORTED";
73
+ readonly RULEPACK_INVALID: "ENGINE_RULEPACK_INVALID";
74
+ readonly ID_EXHAUSTED: "ENGINE_ID_EXHAUSTED";
75
+ readonly CONFIG_INVALID: "ENGINE_CONFIG_INVALID";
76
+ };
77
+ export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
78
+ /** Base class: carries a stable machine-readable code and an actionable hint. */
79
+ export declare class EngineError extends Error {
80
+ readonly code: string;
81
+ readonly hint: string | undefined;
82
+ constructor(code: string, message: string, hint?: string, options?: {
83
+ cause?: unknown;
84
+ });
85
+ }
86
+ /** Bad arguments or illegal preconditions: the model must change its call. */
87
+ export declare class EngineInputError extends EngineError {
88
+ }
89
+ /** Content did not pass a gate: the author must change the manuscript. */
90
+ export declare class EngineBlockedError extends EngineError {
91
+ readonly blocking: BlockingItem[];
92
+ constructor(code: string, message: string, blocking: BlockingItem[], hint?: string);
93
+ }
94
+ /** System fault: the environment or the code is wrong, not the caller. */
95
+ export declare class EngineFaultError extends EngineError {
96
+ }
97
+ /** Convenience constructor set, kept close to the classes so adapters agree. */
98
+ export declare const inputError: (code: string, message: string, hint?: string) => EngineInputError;
99
+ export declare const blockedError: (code: string, message: string, blocking: BlockingItem[], hint?: string) => EngineBlockedError;
100
+ export declare const faultError: (code: string, message: string, cause?: unknown) => EngineFaultError;
101
+ /** One deduction inside one scoring dimension. */
102
+ export interface Deduction {
103
+ rule: string;
104
+ location: string;
105
+ delta: number;
106
+ note: string;
107
+ }
108
+ /** One of the fifteen scoring dimensions. */
109
+ export interface ScoreDimension {
110
+ id: string;
111
+ max: number;
112
+ deductions: Deduction[];
113
+ }
114
+ /** A review issue; blockers cannot be waived. */
115
+ export interface ReviewIssue {
116
+ id: string;
117
+ severity: Severity;
118
+ dimension: string;
119
+ location: string;
120
+ problem: string;
121
+ fix: string;
122
+ status: 'open' | 'fixed' | 'waived';
123
+ evidence?: string;
124
+ }
125
+ /** The persisted review document. */
126
+ export interface ReviewDocument {
127
+ schema_version: 1;
128
+ chapter: number;
129
+ round: number;
130
+ content_hash: string;
131
+ dimensions: ScoreDimension[];
132
+ issues: ReviewIssue[];
133
+ total: number;
134
+ /** The three reading proofs accepted at P4; PG-09 re-verifies them at P6. */
135
+ proof?: ReadingProof;
136
+ }
137
+ /** Structured machine finding. */
138
+ export interface Violation {
139
+ code: string;
140
+ severity: Severity;
141
+ location: {
142
+ paragraph: number;
143
+ };
144
+ detail: string;
145
+ }
146
+ /** Metric block produced by the machine pass. */
147
+ export interface MachineMetrics {
148
+ total_word_count: number;
149
+ non_empty_lines: number;
150
+ dialogue_lines: number;
151
+ dialogue_ratio: number;
152
+ paragraph_max: number;
153
+ }
154
+ /** The persisted check document. */
155
+ export interface CheckDocument {
156
+ schema_version: 1;
157
+ chapter: number;
158
+ scope: string[];
159
+ content_hash: string;
160
+ machine: {
161
+ violations: Violation[];
162
+ metrics: WordMetrics;
163
+ };
164
+ semantic: {
165
+ items: Array<{
166
+ id: string;
167
+ verdict: 'pass' | 'fail';
168
+ evidence: string;
169
+ note: string;
170
+ }>;
171
+ };
172
+ verdict: 'pass' | 'fail';
173
+ }
174
+ /** Word statistics with the single engine-wide counting rule. */
175
+ export interface WordMetrics {
176
+ total_word_count: number;
177
+ cjk_chars: number;
178
+ english_words: number;
179
+ numbers: number;
180
+ chinese_punct: number;
181
+ english_punct: number;
182
+ non_ws_chars: number;
183
+ total_lines: number;
184
+ non_empty_lines: number;
185
+ dialogue_lines: number;
186
+ dialogue_ratio: number;
187
+ paragraph_count: number;
188
+ paragraph_max_words: number;
189
+ }
190
+ /** Derived chapter row kept in `state/chapters.yaml`. */
191
+ export interface ChapterRecord {
192
+ num: number;
193
+ title: string;
194
+ file: string;
195
+ word_count: number;
196
+ dialogue_lines: number;
197
+ dialogue_ratio: number;
198
+ scene_type: SceneType;
199
+ narrator: string;
200
+ score: number;
201
+ status: 'draft' | 'reviewed';
202
+ content_hash: string;
203
+ summary: ChapterSummary;
204
+ /** Keys allocated by the archive transaction; lets a cross-process retry replay. */
205
+ assigned_ids?: Record<string, string>;
206
+ /** Digest of the archive request that produced this row. */
207
+ archive_digest?: string;
208
+ }
209
+ /** Summary block the model supplies with a draft and the engine persists. */
210
+ export interface ChapterSummary {
211
+ events: string[];
212
+ characters_appeared: string[];
213
+ foreshadows: {
214
+ planted: Array<{
215
+ key: string;
216
+ title: string;
217
+ description: string;
218
+ }>;
219
+ advanced: Array<{
220
+ key: string;
221
+ note: string;
222
+ new_info: boolean;
223
+ }>;
224
+ resolved: string[];
225
+ };
226
+ new_elements: {
227
+ locations: string[];
228
+ abilities: string[];
229
+ artifacts: string[];
230
+ };
231
+ resource_changes: Array<{
232
+ character: string;
233
+ resource: string;
234
+ delta: number;
235
+ reason: string;
236
+ }>;
237
+ opening: string;
238
+ continuity: {
239
+ opens_from: string;
240
+ ends_with: string;
241
+ };
242
+ hook_strength: number;
243
+ pleasure_points: string[];
244
+ plot_advancement: 'high' | 'mid' | 'low';
245
+ }
246
+ /** Round counters live in the pipeline and are engine-owned. */
247
+ export interface RoundCounters {
248
+ review: number;
249
+ revise: number;
250
+ check: number;
251
+ }
252
+ /** How one ledger entity changed in this chapter. */
253
+ export type DeltaAction = 'created' | 'updated' | 'no_change';
254
+ /** One character declaration inside `state_delta.characters`. */
255
+ export interface CharacterDelta {
256
+ action: DeltaAction;
257
+ reason?: string;
258
+ role?: string;
259
+ profile?: Record<string, unknown>;
260
+ current_state?: Record<string, unknown>;
261
+ growth?: {
262
+ chapter: number;
263
+ change: string;
264
+ status_change: string;
265
+ };
266
+ }
267
+ /** One world entity declaration inside `state_delta.world`. */
268
+ export interface WorldDelta {
269
+ action: DeltaAction;
270
+ reason?: string;
271
+ kind?: 'location' | 'ability' | 'artifact' | 'faction' | 'rule';
272
+ description?: string;
273
+ layout?: string;
274
+ capacity?: number;
275
+ detail?: Record<string, unknown>;
276
+ }
277
+ /** One foreshadow declaration inside `state_delta.foreshadows`. */
278
+ export interface ForeshadowDelta {
279
+ action: DeltaAction;
280
+ reason?: string;
281
+ title?: string;
282
+ description?: string;
283
+ note?: string;
284
+ new_info?: boolean;
285
+ }
286
+ /** One timeline-event declaration; keys are `summary.events` indices. */
287
+ export interface TimelineDelta {
288
+ action: DeltaAction;
289
+ reason?: string;
290
+ story_time?: string;
291
+ description?: string;
292
+ location?: string;
293
+ characters?: string[];
294
+ }
295
+ /** One relationship declaration; keys are `"a|b"`. */
296
+ export interface RelationshipDelta {
297
+ action: DeltaAction;
298
+ reason?: string;
299
+ a?: string;
300
+ b?: string;
301
+ type?: string;
302
+ description?: string;
303
+ note?: string;
304
+ }
305
+ /**
306
+ * The archive reconciliation sheet. Every entity the chapter claims (appeared
307
+ * characters, new world elements, foreshadow keys, events, relationships) must
308
+ * appear here; omission is a hard error, never an implicit no_change.
309
+ */
310
+ export interface StateDelta {
311
+ characters: Record<string, CharacterDelta>;
312
+ foreshadows: Record<string, ForeshadowDelta>;
313
+ timeline: Record<string, TimelineDelta>;
314
+ world: Record<string, WorldDelta>;
315
+ relationships: Record<string, RelationshipDelta>;
316
+ }
317
+ /** Optional handoff override; omitted values are derived by the engine. */
318
+ export interface ArchiveHandoff {
319
+ volume?: number;
320
+ volume_name?: string;
321
+ act?: string;
322
+ act_progress?: string;
323
+ }
324
+ /** Pipeline document (`state/pipeline.yaml`). */
325
+ export interface PipelineState {
326
+ schema_version: 1;
327
+ revision: number;
328
+ chapter: number;
329
+ stage: Stage;
330
+ rounds: RoundCounters;
331
+ briefs: Record<string, {
332
+ issued_at: string;
333
+ digest: string;
334
+ }>;
335
+ blocking: BlockingItem[];
336
+ last_action: {
337
+ op: string;
338
+ at: string;
339
+ digest: string;
340
+ } | null;
341
+ }
342
+ /** Chapter index document (`state/chapters.yaml`). */
343
+ export interface ChaptersDocument {
344
+ schema_version: 1;
345
+ chapters: ChapterRecord[];
346
+ }
347
+ /** Character registry document. */
348
+ export interface CharactersDocument {
349
+ schema_version: 1;
350
+ characters: Array<{
351
+ name: string;
352
+ role: string;
353
+ first_appearance: number;
354
+ profile: Record<string, unknown>;
355
+ current_state: Record<string, unknown>;
356
+ growth: Array<{
357
+ chapter: number;
358
+ change: string;
359
+ status_change: string;
360
+ }>;
361
+ updated_at: number;
362
+ }>;
363
+ }
364
+ /** World registry document. */
365
+ export interface WorldDocument {
366
+ schema_version: 1;
367
+ locations: Array<{
368
+ name: string;
369
+ first_appearance: number;
370
+ locked: boolean;
371
+ description: string;
372
+ layout: string;
373
+ capacity: number;
374
+ changes: Array<{
375
+ chapter: number;
376
+ change: string;
377
+ reason: string;
378
+ }>;
379
+ }>;
380
+ abilities: unknown[];
381
+ artifacts: unknown[];
382
+ factions: unknown[];
383
+ rules: unknown[];
384
+ }
385
+ /** Locked first-appearance snapshots. */
386
+ export interface LocksDocument {
387
+ schema_version: 1;
388
+ locked_at_chapter: number;
389
+ characters: Record<string, {
390
+ first_appearance: number;
391
+ locked_at: number;
392
+ appearance: string;
393
+ }>;
394
+ locations: Record<string, {
395
+ first_appearance: number;
396
+ locked_at: number;
397
+ layout: string;
398
+ capacity: number;
399
+ }>;
400
+ }
401
+ /** Foreshadow ledger document. */
402
+ export interface ForeshadowsDocument {
403
+ schema_version: 1;
404
+ next_seq: number;
405
+ items: Array<{
406
+ id: string;
407
+ title: string;
408
+ planted: number;
409
+ last_touched: number;
410
+ status: 'planted' | 'dormant' | 'advanced' | 'resolved';
411
+ resolved_at: number | null;
412
+ description: string;
413
+ advance_log: Array<{
414
+ chapter: number;
415
+ note: string;
416
+ new_info: boolean;
417
+ }>;
418
+ }>;
419
+ }
420
+ /** Timeline ledger document. */
421
+ export interface TimelineDocument {
422
+ schema_version: 1;
423
+ next_seq: number;
424
+ items: Array<{
425
+ id: string;
426
+ chapter: number;
427
+ story_time: string;
428
+ description: string;
429
+ location: string;
430
+ characters: string[];
431
+ }>;
432
+ }
433
+ /** Relationship ledger document. */
434
+ export interface RelationshipsDocument {
435
+ schema_version: 1;
436
+ next_seq: number;
437
+ items: Array<{
438
+ id: string;
439
+ a: string;
440
+ b: string;
441
+ type: string;
442
+ since: number;
443
+ description: string;
444
+ evolution: Array<{
445
+ chapter: number;
446
+ note: string;
447
+ }>;
448
+ }>;
449
+ }
450
+ /** Handoff document (`state/handoff.yaml`). */
451
+ export interface HandoffDocument {
452
+ schema_version: 1;
453
+ current_chapter: number;
454
+ volume: number;
455
+ volume_name: string;
456
+ act: string;
457
+ act_progress: string;
458
+ next_chapter: number;
459
+ last_summary: string;
460
+ open_hooks: Array<{
461
+ id: string;
462
+ title: string;
463
+ }>;
464
+ updated_at: string;
465
+ }
466
+ /** Project definition (`novel.yaml`). */
467
+ export interface NovelProject {
468
+ schema_version: 1;
469
+ title: string;
470
+ genre: Genre;
471
+ platform: Platform;
472
+ author: string;
473
+ description: string;
474
+ created_at: string;
475
+ targets: {
476
+ words_per_chapter: [number, number];
477
+ total_chapters: number | null;
478
+ dialogue_ratio: [number, number];
479
+ };
480
+ }
481
+ /** World plan (`plans/chNNN.world.yaml`). */
482
+ export interface WorldPlanInput {
483
+ chapter: number;
484
+ objective: string;
485
+ constraints: string[];
486
+ characters: Array<{
487
+ name: string;
488
+ role: string;
489
+ current_state: string;
490
+ }>;
491
+ locations: Array<{
492
+ name: string;
493
+ layout: string;
494
+ capacity: number;
495
+ }>;
496
+ continuity_notes: string[];
497
+ }
498
+ /** Chapter plan (`plans/chNNN.chapter.yaml`). */
499
+ export interface ChapterPlanInput {
500
+ chapter: number;
501
+ title: string;
502
+ scene_type: SceneType;
503
+ narrator: string;
504
+ beats: string[];
505
+ opening: string;
506
+ ending: string;
507
+ target_words: [number, number];
508
+ }
509
+ /** Draft submission payload. */
510
+ export interface DraftInput {
511
+ chapter: number;
512
+ content: string;
513
+ chapter_title: string;
514
+ scene_type: SceneType;
515
+ narrator: string;
516
+ summary: ChapterSummary;
517
+ }
518
+ /** Review submission payload. */
519
+ export interface ReviewInput {
520
+ chapter: number;
521
+ dimensions: ScoreDimension[];
522
+ issues: ReviewIssue[];
523
+ /** The model's claimed headline score; checked against 150 - Σdelta. */
524
+ total: number;
525
+ proof: ReadingProof;
526
+ }
527
+ /** Revision submission payload. */
528
+ export interface RevisionInput {
529
+ chapter: number;
530
+ content: string;
531
+ summary: ChapterSummary;
532
+ }
533
+ /** Semantic check items supplied by the model. */
534
+ export interface SemanticItem {
535
+ id: string;
536
+ verdict: 'pass' | 'fail';
537
+ evidence: string;
538
+ note: string;
539
+ }
540
+ /** Everything a gate evaluation needs, gathered by the engine before writing. */
541
+ export interface GateInput {
542
+ operation: Operation;
543
+ stage: Stage;
544
+ text: string;
545
+ chapter: number;
546
+ project: NovelProject;
547
+ pipeline: PipelineState;
548
+ chapters: ChaptersDocument;
549
+ briefDigest: string | null;
550
+ briefStage: BriefStage | null;
551
+ draftTitle: string | null;
552
+ sceneType: SceneType | null;
553
+ summary: ChapterSummary | null;
554
+ /** P5 may omit `summary`; when omitted, the previous plan summary is reused. */
555
+ summaryProvided?: boolean;
556
+ review: ReviewDocument | null;
557
+ check: CheckDocument | null;
558
+ semantic: SemanticItem[];
559
+ proof: ReadingProof | null;
560
+ rounds: RoundCounters;
561
+ /** Facts PG-12 checks before the archive transaction starts. */
562
+ archive: {
563
+ chapterExists: boolean;
564
+ checkPassed: boolean;
565
+ hashMatches: boolean;
566
+ } | null;
567
+ evidence: {
568
+ characters: CharactersDocument | null;
569
+ foreshadows: ForeshadowsDocument | null;
570
+ timeline: TimelineDocument | null;
571
+ relationships: RelationshipsDocument | null;
572
+ world: WorldDocument | null;
573
+ locks: LocksDocument | null;
574
+ };
575
+ previousText: string | null;
576
+ /** The three preceding chapters, newest first, for the duplicate rule. */
577
+ previousTexts: string[];
578
+ priorSceneTypes: SceneType[];
579
+ }
580
+ /** Gate evaluation outcome. */
581
+ export interface GateResult {
582
+ passed: boolean;
583
+ blocking: BlockingItem[];
584
+ }
585
+ /** A chapter row projected to the panel and status output. */
586
+ export interface ChapterStatusRow {
587
+ num: number;
588
+ title: string;
589
+ word_count: number;
590
+ score: number;
591
+ stage: Stage;
592
+ passed: boolean;
593
+ }
594
+ /** The `getStatus` payload. */
595
+ export interface StatusReport {
596
+ project: {
597
+ title: string;
598
+ genre: Genre;
599
+ platform: Platform;
600
+ root: string;
601
+ };
602
+ chapter: number;
603
+ stage: Stage;
604
+ rounds: RoundCounters;
605
+ blocking: BlockingItem[];
606
+ metrics: WordMetrics | null;
607
+ next: {
608
+ tool: string;
609
+ args: Record<string, unknown>;
610
+ reason: string;
611
+ };
612
+ }
613
+ /** `advise` answer: which tool to call next and why. */
614
+ export interface AdviceReport {
615
+ stage: Stage;
616
+ chapter: number;
617
+ tool: string;
618
+ args: Record<string, unknown>;
619
+ reason: string;
620
+ blocking: BlockingItem[];
621
+ }
622
+ /** One history row derived from the journal. */
623
+ export interface AuditRow {
624
+ at: string;
625
+ op: string;
626
+ chapter: number;
627
+ revision: number;
628
+ txid: string;
629
+ status: 'committed' | 'aborted';
630
+ files: string[];
631
+ }
632
+ /** One diff hunk between two revisions of a chapter. */
633
+ export interface DiffReport {
634
+ chapter: number;
635
+ before: string;
636
+ after: string;
637
+ added: number;
638
+ removed: number;
639
+ hunks: Array<{
640
+ kind: 'add' | 'remove' | 'same';
641
+ line: string;
642
+ }>;
643
+ }
644
+ /** Export capabilities. */
645
+ export type ExportKind = 'md' | 'txt' | 'epub';