dsh-novel-write-engine 0.0.0-stage → 0.1.1
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/CHANGELOG.md +44 -0
- package/LICENSE +21 -0
- package/README.md +1167 -2
- package/briefs/P1.md +29 -0
- package/briefs/P2.md +34 -0
- package/briefs/P3.md +56 -0
- package/briefs/P4.md +74 -0
- package/briefs/P5.md +26 -0
- package/briefs/P6.md +42 -0
- package/briefs/P8.md +41 -0
- package/cordis.patch.yml +6 -0
- package/lib/client.js +694 -0
- package/lib/index.d.ts +1345 -0
- package/lib/index.js +7469 -0
- package/lib/types/client/dto.d.ts +61 -0
- package/lib/types/client/index.d.ts +17 -0
- package/lib/types/client/panel.d.ts +14 -0
- package/lib/types/client/viewmodel.d.ts +66 -0
- package/lib/types/constants.d.ts +64 -0
- package/lib/types/types.d.ts +650 -0
- package/package.json +135 -4
- package/presets/novel-write-engine.patch.yml +56 -0
- package/rulepacks/default.yaml +231 -0
- package/skills/entry.md +68 -0
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,1345 @@
|
|
|
1
|
+
import Schema from "@deepseek-ai/schemastery";
|
|
2
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
3
|
+
/**
|
|
4
|
+
* Public contracts of the engine. Everything else in `src/` is an implementation
|
|
5
|
+
* detail; adapters (tools, commands, panel, presets) may only import this file
|
|
6
|
+
* and the engine service.
|
|
7
|
+
*/
|
|
8
|
+
/** On-disk schema version for every YAML file the engine owns. */
|
|
9
|
+
declare const SCHEMA_VERSION: 1;
|
|
10
|
+
/** Pipeline control-flow state. This is the state machine value, not a brief id. */
|
|
11
|
+
type Stage = 'idle' | 'P2' | 'P3' | 'P4' | 'P5' | 'P6' | 'ready_to_archive' | 'done';
|
|
12
|
+
/** Brief (task book) ids. `P7`/`P9` are transactions and never hand out a brief. */
|
|
13
|
+
type BriefStage = 'P1' | 'P2' | 'P3' | 'P4' | 'P5' | 'P6' | 'P8';
|
|
14
|
+
/** The twelve supported commercial genres. */
|
|
15
|
+
declare const GENRES: readonly ["玄幻修仙", "都市现代", "西方奇幻", "悬疑推理", "科幻", "游戏系统", "历史架空", "无限流", "轻小说", "军事", "末世流", "言情"];
|
|
16
|
+
type Genre = (typeof GENRES)[number];
|
|
17
|
+
/** Platform style profiles. */
|
|
18
|
+
declare const PLATFORMS: readonly ["fanqie", "qidian", "qimao", "jinjiang"];
|
|
19
|
+
type Platform = (typeof PLATFORMS)[number];
|
|
20
|
+
/** Scene types used by rhythm budgets and PG-06. */
|
|
21
|
+
declare const SCENE_TYPES: readonly ["战斗/动作章", "日常过渡章", "情感爆发章", "纯叙事章"];
|
|
22
|
+
type SceneType = (typeof SCENE_TYPES)[number];
|
|
23
|
+
/** A violation is blocking unless the rule pack declares it advisory. */
|
|
24
|
+
type Severity = 'blocker' | 'advisory';
|
|
25
|
+
/** All twenty-two service operations. */
|
|
26
|
+
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"];
|
|
27
|
+
type Operation = (typeof OPERATIONS)[number];
|
|
28
|
+
/** Structured, non-fatal note attached to a successful result. */
|
|
29
|
+
interface Warning {
|
|
30
|
+
code: string;
|
|
31
|
+
message: string;
|
|
32
|
+
}
|
|
33
|
+
/** One reason a gate refused progress. */
|
|
34
|
+
interface BlockingItem {
|
|
35
|
+
code: string;
|
|
36
|
+
scope?: string;
|
|
37
|
+
detail: string;
|
|
38
|
+
hint?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Uniform success envelope of every service operation. */
|
|
41
|
+
interface OpResult<T> {
|
|
42
|
+
data: T;
|
|
43
|
+
revision: number;
|
|
44
|
+
warnings: Warning[];
|
|
45
|
+
/** True when an idempotent retry was answered from the replay cache. */
|
|
46
|
+
replayed?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** A single reading-proof reference: the quoted text, its paragraph and chapter. */
|
|
49
|
+
interface ReadingProofReference {
|
|
50
|
+
chapter?: number;
|
|
51
|
+
paragraph: number;
|
|
52
|
+
quote: string;
|
|
53
|
+
}
|
|
54
|
+
/** Three references proving the model read the chapter it claims to review. */
|
|
55
|
+
interface ReadingProof {
|
|
56
|
+
opening: ReadingProofReference;
|
|
57
|
+
middle: ReadingProofReference;
|
|
58
|
+
ending: ReadingProofReference;
|
|
59
|
+
}
|
|
60
|
+
/** Error families map one-to-one to the three repair actions a caller can take. */
|
|
61
|
+
declare const ERROR_CODES: {
|
|
62
|
+
readonly PROJECT_NOT_FOUND: "ENGINE_PROJECT_NOT_FOUND";
|
|
63
|
+
readonly PROJECT_EXISTS: "ENGINE_PROJECT_EXISTS";
|
|
64
|
+
readonly STAGE_ORDER: "ENGINE_STAGE_ORDER";
|
|
65
|
+
readonly GATE_BLOCKED: "ENGINE_GATE_BLOCKED";
|
|
66
|
+
readonly READING_PROOF_INVALID: "ENGINE_READING_PROOF_INVALID";
|
|
67
|
+
readonly SCORE_MISMATCH: "ENGINE_SCORE_MISMATCH";
|
|
68
|
+
readonly DELTA_UNVERIFIED: "ENGINE_DELTA_UNVERIFIED";
|
|
69
|
+
readonly RESOURCE_NEGATIVE_BALANCE: "ENGINE_RESOURCE_NEGATIVE_BALANCE";
|
|
70
|
+
readonly SCHEMA_TOO_NEW: "ENGINE_SCHEMA_TOO_NEW";
|
|
71
|
+
readonly SCHEMA_INVALID: "ENGINE_SCHEMA_INVALID";
|
|
72
|
+
readonly PATH_ESCAPE: "ENGINE_PATH_ESCAPE";
|
|
73
|
+
readonly LOCK_TIMEOUT: "ENGINE_LOCK_TIMEOUT";
|
|
74
|
+
readonly TXN_ABORTED: "ENGINE_TXN_ABORTED";
|
|
75
|
+
readonly RULEPACK_INVALID: "ENGINE_RULEPACK_INVALID";
|
|
76
|
+
readonly ID_EXHAUSTED: "ENGINE_ID_EXHAUSTED";
|
|
77
|
+
readonly CONFIG_INVALID: "ENGINE_CONFIG_INVALID";
|
|
78
|
+
};
|
|
79
|
+
type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
|
|
80
|
+
/** Base class: carries a stable machine-readable code and an actionable hint. */
|
|
81
|
+
declare class EngineError extends Error {
|
|
82
|
+
readonly code: string;
|
|
83
|
+
readonly hint: string | undefined;
|
|
84
|
+
constructor(code: string, message: string, hint?: string, options?: {
|
|
85
|
+
cause?: unknown;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** Bad arguments or illegal preconditions: the model must change its call. */
|
|
89
|
+
declare class EngineInputError extends EngineError {}
|
|
90
|
+
/** Content did not pass a gate: the author must change the manuscript. */
|
|
91
|
+
declare class EngineBlockedError extends EngineError {
|
|
92
|
+
readonly blocking: BlockingItem[];
|
|
93
|
+
constructor(code: string, message: string, blocking: BlockingItem[], hint?: string);
|
|
94
|
+
}
|
|
95
|
+
/** System fault: the environment or the code is wrong, not the caller. */
|
|
96
|
+
declare class EngineFaultError extends EngineError {}
|
|
97
|
+
/** Convenience constructor set, kept close to the classes so adapters agree. */
|
|
98
|
+
declare const inputError: (code: string, message: string, hint?: string) => EngineInputError;
|
|
99
|
+
declare const blockedError: (code: string, message: string, blocking: BlockingItem[], hint?: string) => EngineBlockedError;
|
|
100
|
+
declare const faultError: (code: string, message: string, cause?: unknown) => EngineFaultError;
|
|
101
|
+
/** One deduction inside one scoring dimension. */
|
|
102
|
+
interface Deduction {
|
|
103
|
+
rule: string;
|
|
104
|
+
location: string;
|
|
105
|
+
delta: number;
|
|
106
|
+
note: string;
|
|
107
|
+
}
|
|
108
|
+
/** One of the fifteen scoring dimensions. */
|
|
109
|
+
interface ScoreDimension {
|
|
110
|
+
id: string;
|
|
111
|
+
max: number;
|
|
112
|
+
deductions: Deduction[];
|
|
113
|
+
}
|
|
114
|
+
/** A review issue; blockers cannot be waived. */
|
|
115
|
+
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
|
+
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
|
+
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
|
+
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
|
+
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
|
+
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
|
+
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
|
+
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
|
+
interface RoundCounters {
|
|
248
|
+
review: number;
|
|
249
|
+
revise: number;
|
|
250
|
+
check: number;
|
|
251
|
+
}
|
|
252
|
+
/** How one ledger entity changed in this chapter. */
|
|
253
|
+
type DeltaAction = 'created' | 'updated' | 'no_change';
|
|
254
|
+
/** One character declaration inside `state_delta.characters`. */
|
|
255
|
+
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
|
+
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
|
+
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
|
+
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
|
+
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
|
+
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
|
+
interface ArchiveHandoff {
|
|
319
|
+
volume?: number;
|
|
320
|
+
volume_name?: string;
|
|
321
|
+
act?: string;
|
|
322
|
+
act_progress?: string;
|
|
323
|
+
}
|
|
324
|
+
/** Pipeline document (`state/pipeline.yaml`). */
|
|
325
|
+
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
|
+
/** Hash of the review document last written by submitReview/submitRevision. */
|
|
337
|
+
review_digest?: string;
|
|
338
|
+
last_action: {
|
|
339
|
+
op: string;
|
|
340
|
+
at: string;
|
|
341
|
+
digest: string;
|
|
342
|
+
} | null;
|
|
343
|
+
}
|
|
344
|
+
/** Chapter index document (`state/chapters.yaml`). */
|
|
345
|
+
interface ChaptersDocument {
|
|
346
|
+
schema_version: 1;
|
|
347
|
+
chapters: ChapterRecord[];
|
|
348
|
+
}
|
|
349
|
+
/** Character registry document. */
|
|
350
|
+
interface CharactersDocument {
|
|
351
|
+
schema_version: 1;
|
|
352
|
+
characters: Array<{
|
|
353
|
+
name: string;
|
|
354
|
+
role: string;
|
|
355
|
+
first_appearance: number;
|
|
356
|
+
profile: Record<string, unknown>;
|
|
357
|
+
current_state: Record<string, unknown>;
|
|
358
|
+
growth: Array<{
|
|
359
|
+
chapter: number;
|
|
360
|
+
change: string;
|
|
361
|
+
status_change: string;
|
|
362
|
+
}>;
|
|
363
|
+
updated_at: number;
|
|
364
|
+
}>;
|
|
365
|
+
}
|
|
366
|
+
/** World registry document. */
|
|
367
|
+
interface WorldDocument {
|
|
368
|
+
schema_version: 1;
|
|
369
|
+
locations: Array<{
|
|
370
|
+
name: string;
|
|
371
|
+
first_appearance: number;
|
|
372
|
+
locked: boolean;
|
|
373
|
+
description: string;
|
|
374
|
+
layout: string;
|
|
375
|
+
capacity: number;
|
|
376
|
+
changes: Array<{
|
|
377
|
+
chapter: number;
|
|
378
|
+
change: string;
|
|
379
|
+
reason: string;
|
|
380
|
+
}>;
|
|
381
|
+
}>;
|
|
382
|
+
abilities: unknown[];
|
|
383
|
+
artifacts: unknown[];
|
|
384
|
+
factions: unknown[];
|
|
385
|
+
rules: unknown[];
|
|
386
|
+
}
|
|
387
|
+
/** Locked first-appearance snapshots. */
|
|
388
|
+
interface LocksDocument {
|
|
389
|
+
schema_version: 1;
|
|
390
|
+
locked_at_chapter: number;
|
|
391
|
+
characters: Record<string, {
|
|
392
|
+
first_appearance: number;
|
|
393
|
+
locked_at: number;
|
|
394
|
+
appearance: string;
|
|
395
|
+
}>;
|
|
396
|
+
locations: Record<string, {
|
|
397
|
+
first_appearance: number;
|
|
398
|
+
locked_at: number;
|
|
399
|
+
layout: string;
|
|
400
|
+
capacity: number;
|
|
401
|
+
}>;
|
|
402
|
+
}
|
|
403
|
+
/** Foreshadow ledger document. */
|
|
404
|
+
interface ForeshadowsDocument {
|
|
405
|
+
schema_version: 1;
|
|
406
|
+
next_seq: number;
|
|
407
|
+
items: Array<{
|
|
408
|
+
id: string;
|
|
409
|
+
title: string;
|
|
410
|
+
planted: number;
|
|
411
|
+
last_touched: number;
|
|
412
|
+
status: 'planted' | 'dormant' | 'advanced' | 'resolved';
|
|
413
|
+
resolved_at: number | null;
|
|
414
|
+
description: string;
|
|
415
|
+
advance_log: Array<{
|
|
416
|
+
chapter: number;
|
|
417
|
+
note: string;
|
|
418
|
+
new_info: boolean;
|
|
419
|
+
}>;
|
|
420
|
+
}>;
|
|
421
|
+
}
|
|
422
|
+
/** Timeline ledger document. */
|
|
423
|
+
interface TimelineDocument {
|
|
424
|
+
schema_version: 1;
|
|
425
|
+
next_seq: number;
|
|
426
|
+
items: Array<{
|
|
427
|
+
id: string;
|
|
428
|
+
chapter: number;
|
|
429
|
+
story_time: string;
|
|
430
|
+
description: string;
|
|
431
|
+
location: string;
|
|
432
|
+
characters: string[];
|
|
433
|
+
}>;
|
|
434
|
+
}
|
|
435
|
+
/** Relationship ledger document. */
|
|
436
|
+
interface RelationshipsDocument {
|
|
437
|
+
schema_version: 1;
|
|
438
|
+
next_seq: number;
|
|
439
|
+
items: Array<{
|
|
440
|
+
id: string;
|
|
441
|
+
a: string;
|
|
442
|
+
b: string;
|
|
443
|
+
type: string;
|
|
444
|
+
since: number;
|
|
445
|
+
description: string;
|
|
446
|
+
evolution: Array<{
|
|
447
|
+
chapter: number;
|
|
448
|
+
note: string;
|
|
449
|
+
}>;
|
|
450
|
+
}>;
|
|
451
|
+
}
|
|
452
|
+
/** Handoff document (`state/handoff.yaml`). */
|
|
453
|
+
interface HandoffDocument {
|
|
454
|
+
schema_version: 1;
|
|
455
|
+
current_chapter: number;
|
|
456
|
+
volume: number;
|
|
457
|
+
volume_name: string;
|
|
458
|
+
act: string;
|
|
459
|
+
act_progress: string;
|
|
460
|
+
next_chapter: number;
|
|
461
|
+
last_summary: string;
|
|
462
|
+
open_hooks: Array<{
|
|
463
|
+
id: string;
|
|
464
|
+
title: string;
|
|
465
|
+
}>;
|
|
466
|
+
updated_at: string;
|
|
467
|
+
}
|
|
468
|
+
/** Project definition (`novel.yaml`). */
|
|
469
|
+
interface NovelProject {
|
|
470
|
+
schema_version: 1;
|
|
471
|
+
title: string;
|
|
472
|
+
genre: Genre;
|
|
473
|
+
platform: Platform;
|
|
474
|
+
author: string;
|
|
475
|
+
description: string;
|
|
476
|
+
created_at: string;
|
|
477
|
+
targets: {
|
|
478
|
+
words_per_chapter: [number, number];
|
|
479
|
+
total_chapters: number | null;
|
|
480
|
+
dialogue_ratio: [number, number];
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
/** World plan (`plans/chNNN.world.yaml`). */
|
|
484
|
+
interface WorldPlanInput {
|
|
485
|
+
chapter: number;
|
|
486
|
+
objective: string;
|
|
487
|
+
constraints: string[];
|
|
488
|
+
characters: Array<{
|
|
489
|
+
name: string;
|
|
490
|
+
role: string;
|
|
491
|
+
current_state: string;
|
|
492
|
+
}>;
|
|
493
|
+
locations: Array<{
|
|
494
|
+
name: string;
|
|
495
|
+
layout: string;
|
|
496
|
+
capacity: number;
|
|
497
|
+
}>;
|
|
498
|
+
continuity_notes: string[];
|
|
499
|
+
}
|
|
500
|
+
/** Chapter plan (`plans/chNNN.chapter.yaml`). */
|
|
501
|
+
interface ChapterPlanInput {
|
|
502
|
+
chapter: number;
|
|
503
|
+
title: string;
|
|
504
|
+
scene_type: SceneType;
|
|
505
|
+
narrator: string;
|
|
506
|
+
beats: string[];
|
|
507
|
+
opening: string;
|
|
508
|
+
ending: string;
|
|
509
|
+
target_words: [number, number];
|
|
510
|
+
}
|
|
511
|
+
/** Draft submission payload. */
|
|
512
|
+
interface DraftInput {
|
|
513
|
+
chapter: number;
|
|
514
|
+
content: string;
|
|
515
|
+
chapter_title: string;
|
|
516
|
+
scene_type: SceneType;
|
|
517
|
+
narrator: string;
|
|
518
|
+
summary: ChapterSummary;
|
|
519
|
+
}
|
|
520
|
+
/** Review submission payload. */
|
|
521
|
+
interface ReviewInput {
|
|
522
|
+
chapter: number;
|
|
523
|
+
dimensions: ScoreDimension[];
|
|
524
|
+
issues: ReviewIssue[];
|
|
525
|
+
/** The model's claimed headline score; checked against 150 - Σdelta. */
|
|
526
|
+
total: number;
|
|
527
|
+
proof: ReadingProof;
|
|
528
|
+
}
|
|
529
|
+
/** Revision submission payload. */
|
|
530
|
+
interface RevisionInput {
|
|
531
|
+
chapter: number;
|
|
532
|
+
content: string;
|
|
533
|
+
summary: ChapterSummary;
|
|
534
|
+
}
|
|
535
|
+
/** Semantic check items supplied by the model. */
|
|
536
|
+
interface SemanticItem {
|
|
537
|
+
id: string;
|
|
538
|
+
verdict: 'pass' | 'fail';
|
|
539
|
+
evidence: string;
|
|
540
|
+
note: string;
|
|
541
|
+
}
|
|
542
|
+
/** Everything a gate evaluation needs, gathered by the engine before writing. */
|
|
543
|
+
interface GateInput {
|
|
544
|
+
operation: Operation;
|
|
545
|
+
stage: Stage;
|
|
546
|
+
text: string;
|
|
547
|
+
chapter: number;
|
|
548
|
+
project: NovelProject;
|
|
549
|
+
pipeline: PipelineState;
|
|
550
|
+
chapters: ChaptersDocument;
|
|
551
|
+
briefDigest: string | null;
|
|
552
|
+
briefStage: BriefStage | null;
|
|
553
|
+
draftTitle: string | null;
|
|
554
|
+
sceneType: SceneType | null;
|
|
555
|
+
summary: ChapterSummary | null;
|
|
556
|
+
/** P5 may omit `summary`; when omitted, the previous plan summary is reused. */
|
|
557
|
+
summaryProvided?: boolean;
|
|
558
|
+
review: ReviewDocument | null;
|
|
559
|
+
check: CheckDocument | null;
|
|
560
|
+
semantic: SemanticItem[];
|
|
561
|
+
proof: ReadingProof | null;
|
|
562
|
+
rounds: RoundCounters;
|
|
563
|
+
/** Facts PG-12 checks before the archive transaction starts. */
|
|
564
|
+
archive: {
|
|
565
|
+
chapterExists: boolean;
|
|
566
|
+
checkPassed: boolean;
|
|
567
|
+
hashMatches: boolean;
|
|
568
|
+
} | null;
|
|
569
|
+
evidence: {
|
|
570
|
+
characters: CharactersDocument | null;
|
|
571
|
+
foreshadows: ForeshadowsDocument | null;
|
|
572
|
+
timeline: TimelineDocument | null;
|
|
573
|
+
relationships: RelationshipsDocument | null;
|
|
574
|
+
world: WorldDocument | null;
|
|
575
|
+
locks: LocksDocument | null;
|
|
576
|
+
};
|
|
577
|
+
previousText: string | null;
|
|
578
|
+
/** The three preceding chapters, newest first, for the duplicate rule. */
|
|
579
|
+
previousTexts: string[];
|
|
580
|
+
priorSceneTypes: SceneType[];
|
|
581
|
+
}
|
|
582
|
+
/** Gate evaluation outcome. */
|
|
583
|
+
interface GateResult {
|
|
584
|
+
passed: boolean;
|
|
585
|
+
blocking: BlockingItem[];
|
|
586
|
+
}
|
|
587
|
+
/** A chapter row projected to the panel and status output. */
|
|
588
|
+
interface ChapterStatusRow {
|
|
589
|
+
num: number;
|
|
590
|
+
title: string;
|
|
591
|
+
word_count: number;
|
|
592
|
+
score: number;
|
|
593
|
+
stage: Stage;
|
|
594
|
+
passed: boolean;
|
|
595
|
+
}
|
|
596
|
+
/** The `getStatus` payload. */
|
|
597
|
+
interface StatusReport {
|
|
598
|
+
project: {
|
|
599
|
+
title: string;
|
|
600
|
+
genre: Genre;
|
|
601
|
+
platform: Platform;
|
|
602
|
+
root: string;
|
|
603
|
+
};
|
|
604
|
+
/** The chapter requested for metrics; may be historical. */
|
|
605
|
+
chapter: number;
|
|
606
|
+
/** The chapter the pipeline is currently working on. */
|
|
607
|
+
active_chapter: number;
|
|
608
|
+
stage: Stage;
|
|
609
|
+
rounds: RoundCounters;
|
|
610
|
+
blocking: BlockingItem[];
|
|
611
|
+
metrics: WordMetrics | null;
|
|
612
|
+
next: {
|
|
613
|
+
tool: string;
|
|
614
|
+
args: Record<string, unknown>;
|
|
615
|
+
reason: string;
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
/** `advise` answer: which tool to call next and why. */
|
|
619
|
+
interface AdviceReport {
|
|
620
|
+
stage: Stage;
|
|
621
|
+
chapter: number;
|
|
622
|
+
tool: string;
|
|
623
|
+
args: Record<string, unknown>;
|
|
624
|
+
reason: string;
|
|
625
|
+
blocking: BlockingItem[];
|
|
626
|
+
}
|
|
627
|
+
/** One history row derived from the journal. */
|
|
628
|
+
interface AuditRow {
|
|
629
|
+
at: string;
|
|
630
|
+
op: string;
|
|
631
|
+
chapter: number;
|
|
632
|
+
revision: number;
|
|
633
|
+
txid: string;
|
|
634
|
+
status: 'committed' | 'aborted';
|
|
635
|
+
files: string[];
|
|
636
|
+
}
|
|
637
|
+
/** One diff hunk between two revisions of a chapter. */
|
|
638
|
+
interface DiffReport {
|
|
639
|
+
chapter: number;
|
|
640
|
+
before: string;
|
|
641
|
+
after: string;
|
|
642
|
+
added: number;
|
|
643
|
+
removed: number;
|
|
644
|
+
hunks: Array<{
|
|
645
|
+
kind: 'add' | 'remove' | 'same';
|
|
646
|
+
line: string;
|
|
647
|
+
}>;
|
|
648
|
+
}
|
|
649
|
+
/** Export capabilities. */
|
|
650
|
+
type ExportKind = 'md' | 'txt' | 'epub';
|
|
651
|
+
/** One blacklisted word. */
|
|
652
|
+
interface TermEntry {
|
|
653
|
+
term: string;
|
|
654
|
+
note?: string;
|
|
655
|
+
/** Allow the term inside a dialogue-dominant paragraph (character voice). */
|
|
656
|
+
dialogueExempt?: boolean;
|
|
657
|
+
}
|
|
658
|
+
/** A soft-limit word, optionally relaxed for some genres. */
|
|
659
|
+
interface LimitEntry {
|
|
660
|
+
term: string;
|
|
661
|
+
limit: number;
|
|
662
|
+
genres?: Genre[];
|
|
663
|
+
}
|
|
664
|
+
/** Structured rule pack: all taste lives here, never in code. */
|
|
665
|
+
interface Rulepack {
|
|
666
|
+
schema_version: 1;
|
|
667
|
+
name: string;
|
|
668
|
+
version: string;
|
|
669
|
+
lexicon: {
|
|
670
|
+
zero_tolerance: TermEntry[];
|
|
671
|
+
soft: LimitEntry[];
|
|
672
|
+
contextual: LimitEntry[];
|
|
673
|
+
meta_narration: TermEntry[];
|
|
674
|
+
reader_intrusion: Array<{
|
|
675
|
+
pattern: string;
|
|
676
|
+
}>;
|
|
677
|
+
god_view: Array<{
|
|
678
|
+
pattern: string;
|
|
679
|
+
}>;
|
|
680
|
+
conclusion_first: {
|
|
681
|
+
verbs: string[];
|
|
682
|
+
causals: string[];
|
|
683
|
+
};
|
|
684
|
+
};
|
|
685
|
+
format: {
|
|
686
|
+
paragraph_max_words: number;
|
|
687
|
+
forbid_markdown: boolean;
|
|
688
|
+
forbid_semicolon: boolean;
|
|
689
|
+
forbid_english_ellipsis: boolean;
|
|
690
|
+
title: {
|
|
691
|
+
pattern: string;
|
|
692
|
+
min_chars: number;
|
|
693
|
+
max_chars: number;
|
|
694
|
+
};
|
|
695
|
+
system_labels: string[];
|
|
696
|
+
scene_separators: string[];
|
|
697
|
+
markdown_markers: string[];
|
|
698
|
+
};
|
|
699
|
+
punctuation: {
|
|
700
|
+
dash: {
|
|
701
|
+
base: Record<SceneType, number>;
|
|
702
|
+
scale_words: number;
|
|
703
|
+
token: string;
|
|
704
|
+
};
|
|
705
|
+
period_density: {
|
|
706
|
+
min: number;
|
|
707
|
+
max: number;
|
|
708
|
+
};
|
|
709
|
+
ellipsis_run: {
|
|
710
|
+
max: number;
|
|
711
|
+
token: string;
|
|
712
|
+
};
|
|
713
|
+
uniform_sentence: {
|
|
714
|
+
min_sentences: number;
|
|
715
|
+
ratio: number;
|
|
716
|
+
};
|
|
717
|
+
consecutive_le: {
|
|
718
|
+
max: number;
|
|
719
|
+
char: string;
|
|
720
|
+
};
|
|
721
|
+
/** Paragraphs at or above this quoted-character share skip rhythm checks. */
|
|
722
|
+
dialogue_dominant_ratio: number;
|
|
723
|
+
};
|
|
724
|
+
semantics: {
|
|
725
|
+
adjective_density: {
|
|
726
|
+
max: number;
|
|
727
|
+
adjectives: string[];
|
|
728
|
+
};
|
|
729
|
+
pleasure_density: {
|
|
730
|
+
min: number;
|
|
731
|
+
keywords: string[];
|
|
732
|
+
window: number;
|
|
733
|
+
};
|
|
734
|
+
sensory_density: {
|
|
735
|
+
min: number;
|
|
736
|
+
senses: string[];
|
|
737
|
+
window: number;
|
|
738
|
+
};
|
|
739
|
+
water_words: {
|
|
740
|
+
window: number;
|
|
741
|
+
threshold: number;
|
|
742
|
+
scene_thresholds?: Partial<Record<SceneType, number>>;
|
|
743
|
+
terms: string[];
|
|
744
|
+
};
|
|
745
|
+
};
|
|
746
|
+
/** Platform style profiles; `initProject` uses these when the caller omits targets. */
|
|
747
|
+
platform: Record<Platform, {
|
|
748
|
+
words_per_chapter: [number, number];
|
|
749
|
+
dialogue_ratio: [number, number];
|
|
750
|
+
}>;
|
|
751
|
+
/** Genre guides injected into the task-book context; no threshold branches on them. */
|
|
752
|
+
genres: Record<Genre, {
|
|
753
|
+
note: string;
|
|
754
|
+
}>;
|
|
755
|
+
crosschapter: {
|
|
756
|
+
duplicate_similarity: number;
|
|
757
|
+
duplicate_advisory: number;
|
|
758
|
+
/** Paragraphs shorter than this many non-whitespace chars are not duplicate candidates. */
|
|
759
|
+
duplicate_min_chars: number;
|
|
760
|
+
foreshadow_min_dormancy: number;
|
|
761
|
+
foreshadow_dormant_after: number;
|
|
762
|
+
check_time: boolean;
|
|
763
|
+
lock_similarity: number;
|
|
764
|
+
};
|
|
765
|
+
quality: {
|
|
766
|
+
dimensions: Array<{
|
|
767
|
+
id: string;
|
|
768
|
+
max: number;
|
|
769
|
+
}>;
|
|
770
|
+
};
|
|
771
|
+
exclusions: Array<{
|
|
772
|
+
pair: [string, string];
|
|
773
|
+
family: string;
|
|
774
|
+
}>;
|
|
775
|
+
exemptions: string[];
|
|
776
|
+
}
|
|
777
|
+
/** Every persisted document the engine knows how to load. */
|
|
778
|
+
type DocKind = 'project' | 'pipeline' | 'chapters' | 'characters' | 'world' | 'locks' | 'foreshadows' | 'timeline' | 'relationships' | 'handoff' | 'review' | 'check' | 'worldPlan' | 'chapterPlan';
|
|
779
|
+
/** One project row in the cross-project registry. */
|
|
780
|
+
interface RegistryEntry {
|
|
781
|
+
root: string;
|
|
782
|
+
title: string;
|
|
783
|
+
updated_at: string;
|
|
784
|
+
}
|
|
785
|
+
/** One rendered task book. */
|
|
786
|
+
interface BriefDocument {
|
|
787
|
+
stage: BriefStage;
|
|
788
|
+
title: string;
|
|
789
|
+
content: string;
|
|
790
|
+
/** sha256 of the source file, so a task book change is visible to callers. */
|
|
791
|
+
contentHash: string;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Task books live on disk (`briefs/*.md`), never in code: changing writing
|
|
795
|
+
* guidance must not require a release.
|
|
796
|
+
*/
|
|
797
|
+
declare class BriefLibrary {
|
|
798
|
+
private readonly overrideDirectory;
|
|
799
|
+
private readonly bundledDirectory;
|
|
800
|
+
constructor(directory?: string);
|
|
801
|
+
/** Absolute path of the override when configured, otherwise the bundled one. */
|
|
802
|
+
pathFor(stage: BriefStage): string;
|
|
803
|
+
load(stage: BriefStage): BriefDocument;
|
|
804
|
+
/** Replace `{{name}}` placeholders; unknown placeholders are left visible. */
|
|
805
|
+
render(stage: BriefStage, variables: Record<string, string | number>): BriefDocument;
|
|
806
|
+
}
|
|
807
|
+
/** Gate identifiers in fixed evaluation order. */
|
|
808
|
+
declare const GATES: {
|
|
809
|
+
readonly 'PG-01': "BRIEF_REQUIRED";
|
|
810
|
+
readonly 'PG-02': "STAGE_ORDER";
|
|
811
|
+
readonly 'PG-03': "WORD_COUNT";
|
|
812
|
+
readonly 'PG-04': "FORMAT";
|
|
813
|
+
readonly 'PG-05': "SUMMARY_SCHEMA";
|
|
814
|
+
readonly 'PG-06': "SCENE_TYPE";
|
|
815
|
+
readonly 'PG-07': "SCORE";
|
|
816
|
+
readonly 'PG-08': "ISSUES_CLEARED";
|
|
817
|
+
readonly 'PG-09': "READING_PROOF";
|
|
818
|
+
readonly 'PG-10': "CONSISTENCY";
|
|
819
|
+
readonly 'PG-11': "ROUNDS";
|
|
820
|
+
readonly 'PG-12': "ARCHIVE_ATOMIC";
|
|
821
|
+
};
|
|
822
|
+
type GateId = keyof typeof GATES;
|
|
823
|
+
/** Everything gate evaluation needs beyond the input: taste comes from the pack. */
|
|
824
|
+
interface GateOptions {
|
|
825
|
+
rulepack: Rulepack;
|
|
826
|
+
minScore: number;
|
|
827
|
+
}
|
|
828
|
+
/** Parse "第 12 段" into 12; null when the location is not a paragraph reference. */
|
|
829
|
+
declare function parseParagraphLocation(location: string): number | null;
|
|
830
|
+
/** Verify one reading-proof reference against the manuscript. */
|
|
831
|
+
declare function verifyReference(text: string, reference: ReadingProofReference): string | null;
|
|
832
|
+
/** Evaluate all twelve gates and return every blocking item, in canonical order. */
|
|
833
|
+
declare function evaluateGates(input: GateInput, options: GateOptions): GateResult;
|
|
834
|
+
/**
|
|
835
|
+
* Which gates apply to an operation. A gate that does not apply must not run:
|
|
836
|
+
* judging word count on a world plan would block every plan.
|
|
837
|
+
*/
|
|
838
|
+
declare function applicableGates(input: GateInput): GateId[];
|
|
839
|
+
/** Human-facing label of a gate id, used by status and the panel. */
|
|
840
|
+
declare function gateName(id: GateId): string;
|
|
841
|
+
/** Which brief stage an operation requires; `null` means no brief gate. */
|
|
842
|
+
declare function briefStageFor(operation: GateInput['operation'], stage: Stage): GateInput['briefStage'];
|
|
843
|
+
/** The shared read model: everything the panel, status and commands render. */
|
|
844
|
+
interface PanelDraft {
|
|
845
|
+
num: number;
|
|
846
|
+
title: string;
|
|
847
|
+
wordCount: number;
|
|
848
|
+
score: number;
|
|
849
|
+
status: 'draft' | 'blocked';
|
|
850
|
+
details: string;
|
|
851
|
+
}
|
|
852
|
+
interface PanelSnapshot {
|
|
853
|
+
root: string;
|
|
854
|
+
title: string;
|
|
855
|
+
genre: string;
|
|
856
|
+
platform: string;
|
|
857
|
+
chapter: number;
|
|
858
|
+
stage: Stage;
|
|
859
|
+
revision: number;
|
|
860
|
+
rounds: {
|
|
861
|
+
review: number;
|
|
862
|
+
revise: number;
|
|
863
|
+
check: number;
|
|
864
|
+
};
|
|
865
|
+
blocking: BlockingItem[];
|
|
866
|
+
/** Recommended next action; lets the panel explain a stage with no blockers. */
|
|
867
|
+
next?: StatusReport['next'];
|
|
868
|
+
/** True only when the configured total chapter target has been reached. */
|
|
869
|
+
finished?: boolean;
|
|
870
|
+
chapters: ChapterRecord[];
|
|
871
|
+
/** The active, not-yet-archived chapter; archived rows come from `chapters`. */
|
|
872
|
+
draft?: PanelDraft | null;
|
|
873
|
+
/** Total words across the index, for the header. */
|
|
874
|
+
totalWords: number;
|
|
875
|
+
/** The nine-phase strip state, derived from the pipeline stage. */
|
|
876
|
+
phases: Array<{
|
|
877
|
+
id: string;
|
|
878
|
+
done: boolean;
|
|
879
|
+
active: boolean;
|
|
880
|
+
}>;
|
|
881
|
+
}
|
|
882
|
+
interface PanelSummaryDto {
|
|
883
|
+
schemaVersion: number;
|
|
884
|
+
panelId: string;
|
|
885
|
+
title: string;
|
|
886
|
+
genre: string;
|
|
887
|
+
platform: string;
|
|
888
|
+
chapter: number;
|
|
889
|
+
stage: Stage;
|
|
890
|
+
revision: number;
|
|
891
|
+
totalWords: number;
|
|
892
|
+
totalChapters: number;
|
|
893
|
+
phases: Array<{
|
|
894
|
+
id: string;
|
|
895
|
+
done: boolean;
|
|
896
|
+
active: boolean;
|
|
897
|
+
}>;
|
|
898
|
+
blocking: BlockingItem[];
|
|
899
|
+
next?: {
|
|
900
|
+
tool: string;
|
|
901
|
+
reason: string;
|
|
902
|
+
};
|
|
903
|
+
/** Absent for older payloads; false means the current chapter cycle is done. */
|
|
904
|
+
finished?: boolean;
|
|
905
|
+
}
|
|
906
|
+
interface PanelChapterRow {
|
|
907
|
+
num: number;
|
|
908
|
+
title: string;
|
|
909
|
+
wordCount: number;
|
|
910
|
+
score: number;
|
|
911
|
+
stage: Stage;
|
|
912
|
+
status: 'passed' | 'blocked' | 'draft';
|
|
913
|
+
/** One-line summary shown when the row is expanded. */
|
|
914
|
+
details: string;
|
|
915
|
+
}
|
|
916
|
+
interface PanelChaptersDto {
|
|
917
|
+
schemaVersion: number;
|
|
918
|
+
panelId: string;
|
|
919
|
+
rows: PanelChapterRow[];
|
|
920
|
+
}
|
|
921
|
+
interface PanelGatesDto {
|
|
922
|
+
schemaVersion: number;
|
|
923
|
+
panelId: string;
|
|
924
|
+
stage: Stage;
|
|
925
|
+
blocking: BlockingItem[];
|
|
926
|
+
/** Human-facing one-liner per blocking item. */
|
|
927
|
+
explanations: string[];
|
|
928
|
+
}
|
|
929
|
+
/** The nine-phase progress strip: phases before the active one are done. */
|
|
930
|
+
declare function phaseStrip(stage: Stage, finished?: boolean): Array<{
|
|
931
|
+
id: string;
|
|
932
|
+
done: boolean;
|
|
933
|
+
active: boolean;
|
|
934
|
+
}>;
|
|
935
|
+
/** Project a chapter row for the table; a reviewed chapter without blockers reads as passed. */
|
|
936
|
+
declare function projectChapter(record: ChapterRecord): PanelChapterRow;
|
|
937
|
+
declare function projectSummary(snapshot: PanelSnapshot): PanelSummaryDto;
|
|
938
|
+
declare function projectChapters(snapshot: PanelSnapshot): PanelChaptersDto;
|
|
939
|
+
declare function projectGates(snapshot: PanelSnapshot): PanelGatesDto;
|
|
940
|
+
/** Plugin configuration, mapped from `Config` in `src/index.ts`. */
|
|
941
|
+
interface EngineConfig {
|
|
942
|
+
projectRoot: string;
|
|
943
|
+
/** Where the multi-project registry lives; never inside a project. */
|
|
944
|
+
stateDir: string;
|
|
945
|
+
tools: {
|
|
946
|
+
mode: 'all' | 'core';
|
|
947
|
+
};
|
|
948
|
+
quality: {
|
|
949
|
+
minScore: number;
|
|
950
|
+
};
|
|
951
|
+
briefs: {
|
|
952
|
+
overrides: string;
|
|
953
|
+
};
|
|
954
|
+
storage: {
|
|
955
|
+
backupRetention: number;
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
/** Events other plugins may subscribe to; payloads never contain manuscript text. */
|
|
959
|
+
type EngineEvent = {
|
|
960
|
+
name: 'novel/archived';
|
|
961
|
+
payload: {
|
|
962
|
+
schemaVersion: 1;
|
|
963
|
+
projectRoot: string;
|
|
964
|
+
chapter: number;
|
|
965
|
+
score: number;
|
|
966
|
+
words: number;
|
|
967
|
+
};
|
|
968
|
+
} | {
|
|
969
|
+
name: 'novel/blocked';
|
|
970
|
+
payload: {
|
|
971
|
+
schemaVersion: 1;
|
|
972
|
+
projectRoot: string;
|
|
973
|
+
operation: string;
|
|
974
|
+
blocking: number;
|
|
975
|
+
};
|
|
976
|
+
} | {
|
|
977
|
+
name: 'novel/amended';
|
|
978
|
+
payload: {
|
|
979
|
+
schemaVersion: 1;
|
|
980
|
+
projectRoot: string;
|
|
981
|
+
path: string;
|
|
982
|
+
reason: string;
|
|
983
|
+
};
|
|
984
|
+
};
|
|
985
|
+
interface InitProjectInput {
|
|
986
|
+
title: string;
|
|
987
|
+
genre: NovelProject['genre'];
|
|
988
|
+
platform: NovelProject['platform'];
|
|
989
|
+
author?: string;
|
|
990
|
+
description?: string;
|
|
991
|
+
words_per_chapter?: [number, number];
|
|
992
|
+
total_chapters?: number | null;
|
|
993
|
+
dialogue_ratio?: [number, number];
|
|
994
|
+
root?: string;
|
|
995
|
+
}
|
|
996
|
+
interface StatusInput {
|
|
997
|
+
chapter?: number;
|
|
998
|
+
root?: string;
|
|
999
|
+
}
|
|
1000
|
+
interface BriefInput {
|
|
1001
|
+
stage: BriefStage;
|
|
1002
|
+
chapter?: number;
|
|
1003
|
+
root?: string;
|
|
1004
|
+
}
|
|
1005
|
+
interface WorldPlanSubmission extends WorldPlanInput {
|
|
1006
|
+
root?: string;
|
|
1007
|
+
}
|
|
1008
|
+
interface ChapterPlanSubmission extends ChapterPlanInput {
|
|
1009
|
+
root?: string;
|
|
1010
|
+
}
|
|
1011
|
+
interface DraftSubmission {
|
|
1012
|
+
chapter: number;
|
|
1013
|
+
content: string;
|
|
1014
|
+
chapter_title: string;
|
|
1015
|
+
scene_type: SceneType;
|
|
1016
|
+
narrator: string;
|
|
1017
|
+
summary: ChapterSummary;
|
|
1018
|
+
root?: string;
|
|
1019
|
+
}
|
|
1020
|
+
interface ReviewSubmission extends ReviewInput {
|
|
1021
|
+
root?: string;
|
|
1022
|
+
}
|
|
1023
|
+
interface RevisionSubmission extends Omit<RevisionInput, 'summary'> {
|
|
1024
|
+
summary?: ChapterSummary;
|
|
1025
|
+
/** Updated issue statuses/evidence; the review document is patched in place. */
|
|
1026
|
+
issues?: ReviewDocument['issues'];
|
|
1027
|
+
/** Proof that the revised text was read; PG-09 verifies it against the new text. */
|
|
1028
|
+
proof: ReadingProof;
|
|
1029
|
+
root?: string;
|
|
1030
|
+
}
|
|
1031
|
+
interface CheckSubmission {
|
|
1032
|
+
chapter: number;
|
|
1033
|
+
/** The four P6 semantic items; omission is a failed check, never a pass. */
|
|
1034
|
+
semantic: SemanticItem[];
|
|
1035
|
+
scope?: string[];
|
|
1036
|
+
root?: string;
|
|
1037
|
+
}
|
|
1038
|
+
interface ArchiveSubmission {
|
|
1039
|
+
chapter: number;
|
|
1040
|
+
/** Mandatory reconciliation sheet for the five derived ledgers. */
|
|
1041
|
+
state_delta: StateDelta;
|
|
1042
|
+
/** Optional volume/act override; omitted values are derived by the engine. */
|
|
1043
|
+
handoff?: ArchiveHandoff;
|
|
1044
|
+
root?: string;
|
|
1045
|
+
}
|
|
1046
|
+
interface AmendSubmission {
|
|
1047
|
+
path: string;
|
|
1048
|
+
content: Record<string, unknown>;
|
|
1049
|
+
reason: string;
|
|
1050
|
+
root?: string;
|
|
1051
|
+
}
|
|
1052
|
+
interface RollbackSubmission {
|
|
1053
|
+
chapter: number;
|
|
1054
|
+
toStage: 'P1' | 'P2' | 'P3' | 'P4' | 'P5' | 'P6';
|
|
1055
|
+
reason?: string;
|
|
1056
|
+
root?: string;
|
|
1057
|
+
}
|
|
1058
|
+
interface DiffInput {
|
|
1059
|
+
chapter: number;
|
|
1060
|
+
before?: string;
|
|
1061
|
+
after?: string;
|
|
1062
|
+
root?: string;
|
|
1063
|
+
}
|
|
1064
|
+
interface AuditInput {
|
|
1065
|
+
limit?: number;
|
|
1066
|
+
root?: string;
|
|
1067
|
+
}
|
|
1068
|
+
interface ExportInput {
|
|
1069
|
+
kind: ExportKind;
|
|
1070
|
+
chapter?: number;
|
|
1071
|
+
root?: string;
|
|
1072
|
+
}
|
|
1073
|
+
interface StateQueryInput {
|
|
1074
|
+
kind: DocKind | 'novel' | 'planWorld' | 'planChapter';
|
|
1075
|
+
chapter?: number;
|
|
1076
|
+
root?: string;
|
|
1077
|
+
}
|
|
1078
|
+
interface WordCountInput {
|
|
1079
|
+
text?: string;
|
|
1080
|
+
chapter?: number;
|
|
1081
|
+
root?: string;
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* The single write authority. Tools, commands, the panel and task books never
|
|
1085
|
+
* touch a file; every mutation is one of these operations.
|
|
1086
|
+
*/
|
|
1087
|
+
declare class EngineService {
|
|
1088
|
+
private readonly config;
|
|
1089
|
+
private readonly stores;
|
|
1090
|
+
private readonly mutex;
|
|
1091
|
+
private readonly replay;
|
|
1092
|
+
private readonly briefs;
|
|
1093
|
+
private readonly now;
|
|
1094
|
+
/** The project this process last touched; the panel follows it without a workspace hint. */
|
|
1095
|
+
private lastRoot;
|
|
1096
|
+
constructor(config: EngineConfig, rulepack: Rulepack | (() => Rulepack), options?: {
|
|
1097
|
+
briefs?: BriefLibrary;
|
|
1098
|
+
now?: () => Date;
|
|
1099
|
+
onEvent?: (event: EngineEvent) => void;
|
|
1100
|
+
});
|
|
1101
|
+
/** Compiled on first use so `apply()` performs no IO. */
|
|
1102
|
+
private readonly rulepackSource;
|
|
1103
|
+
private compiled;
|
|
1104
|
+
private disposed;
|
|
1105
|
+
/**
|
|
1106
|
+
* Release in-process caches on host teardown. The engine owns no file
|
|
1107
|
+
* handles, so this is deliberately synchronous and idempotent; it prevents a
|
|
1108
|
+
* disposed instance from keeping project stores and replay results alive.
|
|
1109
|
+
*/
|
|
1110
|
+
dispose(): void;
|
|
1111
|
+
private get rulepack();
|
|
1112
|
+
private readonly onEvent;
|
|
1113
|
+
private readonly signalContext;
|
|
1114
|
+
/** Bind the host cancellation signal to the engine work of one tool call. */
|
|
1115
|
+
withSignal<T>(signal: AbortSignal | undefined, task: () => Promise<T>): Promise<T>;
|
|
1116
|
+
/** The configured default project root. */
|
|
1117
|
+
defaultRoot(): string;
|
|
1118
|
+
/**
|
|
1119
|
+
* Resolve an omitted root through the cross-project registry. The registry is
|
|
1120
|
+
* convenience state, so a corrupt or unreadable registry falls back to the
|
|
1121
|
+
* configured root instead of making every call fail.
|
|
1122
|
+
*/
|
|
1123
|
+
private resolveRoot;
|
|
1124
|
+
private storeFor;
|
|
1125
|
+
initProject(input: InitProjectInput): Promise<OpResult<{
|
|
1126
|
+
root: string;
|
|
1127
|
+
title: string;
|
|
1128
|
+
chapter: number;
|
|
1129
|
+
stage: Stage;
|
|
1130
|
+
}>>;
|
|
1131
|
+
getStatus(input?: StatusInput): Promise<OpResult<StatusReport>>;
|
|
1132
|
+
countWords(input?: WordCountInput): Promise<OpResult<WordMetrics>>;
|
|
1133
|
+
queryState(input: StateQueryInput): Promise<OpResult<unknown>>;
|
|
1134
|
+
advise(input?: {
|
|
1135
|
+
root?: string;
|
|
1136
|
+
}): Promise<OpResult<{
|
|
1137
|
+
stage: Stage;
|
|
1138
|
+
chapter: number;
|
|
1139
|
+
tool: string;
|
|
1140
|
+
args: Record<string, unknown>;
|
|
1141
|
+
reason: string;
|
|
1142
|
+
blocking: BlockingItem[];
|
|
1143
|
+
}>>;
|
|
1144
|
+
getBrief(input: BriefInput): Promise<OpResult<{
|
|
1145
|
+
stage: string;
|
|
1146
|
+
chapter: number;
|
|
1147
|
+
digest: string;
|
|
1148
|
+
title: string;
|
|
1149
|
+
content: string;
|
|
1150
|
+
content_hash: string;
|
|
1151
|
+
fields: Record<string, unknown>;
|
|
1152
|
+
context: Record<string, unknown>;
|
|
1153
|
+
}>>;
|
|
1154
|
+
submitWorldPlan(input: WorldPlanSubmission): Promise<OpResult<{
|
|
1155
|
+
chapter: number;
|
|
1156
|
+
stage: Stage;
|
|
1157
|
+
plan_file: string;
|
|
1158
|
+
}>>;
|
|
1159
|
+
submitChapterPlan(input: ChapterPlanSubmission): Promise<OpResult<{
|
|
1160
|
+
chapter: number;
|
|
1161
|
+
stage: Stage;
|
|
1162
|
+
plan_file: string;
|
|
1163
|
+
}>>;
|
|
1164
|
+
submitDraft(input: DraftSubmission): Promise<OpResult<{
|
|
1165
|
+
chapter: number;
|
|
1166
|
+
stage: Stage;
|
|
1167
|
+
metrics: WordMetrics;
|
|
1168
|
+
warnings: Warning[];
|
|
1169
|
+
}>>;
|
|
1170
|
+
submitReview(input: ReviewSubmission): Promise<OpResult<{
|
|
1171
|
+
chapter: number;
|
|
1172
|
+
round: number;
|
|
1173
|
+
total: number;
|
|
1174
|
+
open_blockers: number;
|
|
1175
|
+
next_stage: Stage;
|
|
1176
|
+
}>>;
|
|
1177
|
+
submitRevision(input: RevisionSubmission): Promise<OpResult<{
|
|
1178
|
+
chapter: number;
|
|
1179
|
+
stage: Stage;
|
|
1180
|
+
metrics: WordMetrics;
|
|
1181
|
+
}>>;
|
|
1182
|
+
runCheck(input: CheckSubmission): Promise<OpResult<{
|
|
1183
|
+
chapter: number;
|
|
1184
|
+
verdict: 'pass' | 'fail';
|
|
1185
|
+
violations: number;
|
|
1186
|
+
metrics: WordMetrics;
|
|
1187
|
+
stage: Stage;
|
|
1188
|
+
}>>;
|
|
1189
|
+
archiveChapter(input: ArchiveSubmission): Promise<OpResult<{
|
|
1190
|
+
chapter: number;
|
|
1191
|
+
assigned_ids: Record<string, string>;
|
|
1192
|
+
metrics: WordMetrics;
|
|
1193
|
+
score: number;
|
|
1194
|
+
}>>;
|
|
1195
|
+
amend(input: AmendSubmission): Promise<OpResult<{
|
|
1196
|
+
path: string;
|
|
1197
|
+
revision: number;
|
|
1198
|
+
}>>;
|
|
1199
|
+
rollback(input: RollbackSubmission): Promise<OpResult<{
|
|
1200
|
+
chapter: number;
|
|
1201
|
+
stage: Stage;
|
|
1202
|
+
}>>;
|
|
1203
|
+
diff(input: DiffInput): Promise<OpResult<DiffReport>>;
|
|
1204
|
+
auditHistory(input?: AuditInput): Promise<OpResult<AuditRow[]>>;
|
|
1205
|
+
export(input: ExportInput): Promise<OpResult<{
|
|
1206
|
+
kind: ExportKind;
|
|
1207
|
+
files: string[];
|
|
1208
|
+
}>>;
|
|
1209
|
+
panelSummary(input?: {
|
|
1210
|
+
root?: string;
|
|
1211
|
+
}): Promise<PanelSummaryDto>;
|
|
1212
|
+
panelChapters(input?: {
|
|
1213
|
+
root?: string;
|
|
1214
|
+
}): Promise<PanelChaptersDto>;
|
|
1215
|
+
panelGates(input?: {
|
|
1216
|
+
root?: string;
|
|
1217
|
+
}): Promise<PanelGatesDto>;
|
|
1218
|
+
/** One snapshot, three projections: the panel never mixes revisions. */
|
|
1219
|
+
panelBundle(input?: {
|
|
1220
|
+
root?: string;
|
|
1221
|
+
}): Promise<{
|
|
1222
|
+
summary: PanelSummaryDto;
|
|
1223
|
+
chapters: PanelChaptersDto;
|
|
1224
|
+
gates: PanelGatesDto;
|
|
1225
|
+
}>;
|
|
1226
|
+
stats(input?: {
|
|
1227
|
+
root?: string;
|
|
1228
|
+
}): Promise<{
|
|
1229
|
+
totalWords: number;
|
|
1230
|
+
chapters: number;
|
|
1231
|
+
revision: number;
|
|
1232
|
+
rulepack: string;
|
|
1233
|
+
averageScore: number;
|
|
1234
|
+
lastScore: number;
|
|
1235
|
+
openForeshadows: number;
|
|
1236
|
+
}>;
|
|
1237
|
+
private registry;
|
|
1238
|
+
/** List known projects (convenience only; a project opens without a row). */
|
|
1239
|
+
listProjects(): Promise<{
|
|
1240
|
+
active: string | null;
|
|
1241
|
+
projects: RegistryEntry[];
|
|
1242
|
+
}>;
|
|
1243
|
+
/** Record the current project in the registry. */
|
|
1244
|
+
registerProject(input?: {
|
|
1245
|
+
root?: string;
|
|
1246
|
+
}): Promise<{
|
|
1247
|
+
active: string | null;
|
|
1248
|
+
projects: RegistryEntry[];
|
|
1249
|
+
}>;
|
|
1250
|
+
/** Choose the default project for future convenience calls. */
|
|
1251
|
+
setActiveProject(input: {
|
|
1252
|
+
root: string;
|
|
1253
|
+
}): Promise<{
|
|
1254
|
+
active: string | null;
|
|
1255
|
+
}>;
|
|
1256
|
+
/** Forget a registry row; the project directory is never touched. */
|
|
1257
|
+
forgetProject(input: {
|
|
1258
|
+
root: string;
|
|
1259
|
+
}): Promise<{
|
|
1260
|
+
active: string | null;
|
|
1261
|
+
projects: RegistryEntry[];
|
|
1262
|
+
}>;
|
|
1263
|
+
/**
|
|
1264
|
+
* Import one ledger from a settings pack. Always previews with a diff; a
|
|
1265
|
+
* dry run never writes, and an apply is a normal transaction.
|
|
1266
|
+
*/
|
|
1267
|
+
importPack(input: {
|
|
1268
|
+
kind: 'characters' | 'world' | 'foreshadows' | 'relationships' | 'timeline';
|
|
1269
|
+
content: unknown;
|
|
1270
|
+
dryRun?: boolean;
|
|
1271
|
+
root?: string;
|
|
1272
|
+
}): Promise<OpResult<{
|
|
1273
|
+
kind: string;
|
|
1274
|
+
preview: string;
|
|
1275
|
+
applied: boolean;
|
|
1276
|
+
}>>;
|
|
1277
|
+
private panelSnapshot;
|
|
1278
|
+
private nextStep;
|
|
1279
|
+
/** A computed blocker for the retry budget the next operation would consume. */
|
|
1280
|
+
private roundsLimitBlock;
|
|
1281
|
+
/** Pipeline blockers plus budget exhaustion, so status/panel cannot say "all clear". */
|
|
1282
|
+
private effectiveBlocking;
|
|
1283
|
+
/** Whether the stored brief still signs the current snapshot. */
|
|
1284
|
+
private briefIsCurrent;
|
|
1285
|
+
/** Arguments valid for the tool `STAGE_NEXT` recommends at this stage. */
|
|
1286
|
+
private nextArgs;
|
|
1287
|
+
private metricsFor;
|
|
1288
|
+
private readChapter;
|
|
1289
|
+
private latestBackup;
|
|
1290
|
+
private chaptersForExport;
|
|
1291
|
+
private machine;
|
|
1292
|
+
private briefContextFor;
|
|
1293
|
+
private briefDigest;
|
|
1294
|
+
private briefVariables;
|
|
1295
|
+
private assertGates;
|
|
1296
|
+
private assertChapterParameter;
|
|
1297
|
+
private assertChapter;
|
|
1298
|
+
private load;
|
|
1299
|
+
private mutate;
|
|
1300
|
+
private invalidateReplay;
|
|
1301
|
+
private emptySnapshot;
|
|
1302
|
+
}
|
|
1303
|
+
/** Read, compile and freeze a pack. Runtime callers always use the bundled default. */
|
|
1304
|
+
declare function loadRulepack(path?: string): Rulepack;
|
|
1305
|
+
/** One rendered chapter, assembled by the engine before rendering. */
|
|
1306
|
+
interface ExportChapter {
|
|
1307
|
+
num: number;
|
|
1308
|
+
title: string;
|
|
1309
|
+
text: string;
|
|
1310
|
+
}
|
|
1311
|
+
/** `第 N 章 标题` — the title stored in the index never carries the prefix. */
|
|
1312
|
+
declare function chapterHeading(chapter: Pick<ChapterRecord, 'num' | 'title'>): string;
|
|
1313
|
+
/** Markdown export: one heading per chapter, body unchanged (it is plain text). */
|
|
1314
|
+
declare function renderMarkdown(project: NovelProject, chapters: readonly ExportChapter[]): string;
|
|
1315
|
+
/** Plain-text export: no markdown syntax, no BOM. */
|
|
1316
|
+
declare function renderPlainText(project: NovelProject, chapters: readonly ExportChapter[]): string;
|
|
1317
|
+
/** Blurb built from the project record; deterministic, no model call. */
|
|
1318
|
+
declare function renderBlurb(project: NovelProject): string;
|
|
1319
|
+
/** Cover as a standalone SVG string; text is escaped, palette is theme-neutral. */
|
|
1320
|
+
declare function renderCover(project: NovelProject): string;
|
|
1321
|
+
/** XHTML chapter document for the EPUB package. */
|
|
1322
|
+
declare function renderEpubChapter(chapter: ExportChapter): string;
|
|
1323
|
+
declare const name = "dsh-novel-write-engine";
|
|
1324
|
+
declare const inject: string[];
|
|
1325
|
+
/** Plugin configuration. Every value is also readable in `--dump-config`. */
|
|
1326
|
+
interface Config {
|
|
1327
|
+
projectRoot: string;
|
|
1328
|
+
minScore: number;
|
|
1329
|
+
/** `all` or `core`; validated in `apply`, so the schema stays a plain string. */
|
|
1330
|
+
toolsMode: string;
|
|
1331
|
+
/** Optional directory with user task-book overrides; empty uses the bundled set. */
|
|
1332
|
+
briefsOverrides: string;
|
|
1333
|
+
/** How many archived-chapter backups to keep. */
|
|
1334
|
+
backupRetention: number;
|
|
1335
|
+
/** Directory for cross-project bookkeeping; never inside a project. */
|
|
1336
|
+
stateDir: string;
|
|
1337
|
+
}
|
|
1338
|
+
declare const Config: Schema<Config>;
|
|
1339
|
+
/**
|
|
1340
|
+
* Assemble the engine and its four thin adapters. No IO happens here: the rule
|
|
1341
|
+
* pack compiles on first use, task books load on demand, and the store is
|
|
1342
|
+
* created lazily per project root.
|
|
1343
|
+
*/
|
|
1344
|
+
declare function apply(ctx: Context, config: Config): void;
|
|
1345
|
+
export { AdviceReport, ArchiveHandoff, AuditRow, BlockingItem, BriefDocument, BriefLibrary, BriefStage, ChapterPlanInput, ChapterRecord, ChapterStatusRow, ChapterSummary, ChaptersDocument, CharacterDelta, CharactersDocument, CheckDocument, Config, Deduction, DeltaAction, DiffReport, DraftInput, ERROR_CODES, EngineBlockedError, EngineError, EngineFaultError, EngineInputError, EngineService, ErrorCode, ExportChapter, ExportKind, ForeshadowDelta, ForeshadowsDocument, GENRES, GateInput, GateOptions, GateResult, Genre, HandoffDocument, LocksDocument, MachineMetrics, NovelProject, OPERATIONS, OpResult, Operation, PLATFORMS, PanelChapterRow, PanelChaptersDto, PanelDraft, PanelGatesDto, PanelSnapshot, PanelSummaryDto, PipelineState, Platform, ReadingProof, ReadingProofReference, RelationshipDelta, RelationshipsDocument, ReviewDocument, ReviewInput, ReviewIssue, RevisionInput, RoundCounters, SCENE_TYPES, SCHEMA_VERSION, SceneType, ScoreDimension, SemanticItem, Severity, Stage, StateDelta, StatusReport, TimelineDelta, TimelineDocument, Violation, Warning, WordMetrics, WorldDelta, WorldDocument, WorldPlanInput, applicableGates, apply, blockedError, briefStageFor, chapterHeading, evaluateGates, faultError, gateName, inject, inputError, loadRulepack, name, parseParagraphLocation, phaseStrip, projectChapter, projectChapters, projectGates, projectSummary, renderBlurb, renderCover, renderEpubChapter, renderMarkdown, renderPlainText, verifyReference };
|