@narrative-os/engine 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.
Files changed (71) hide show
  1. package/dist/agents/canonValidator.d.ts +9 -0
  2. package/dist/agents/canonValidator.js +51 -0
  3. package/dist/agents/chapterPlanner.d.ts +50 -0
  4. package/dist/agents/chapterPlanner.js +250 -0
  5. package/dist/agents/completeness.d.ts +7 -0
  6. package/dist/agents/completeness.js +51 -0
  7. package/dist/agents/memoryExtractor.d.ts +12 -0
  8. package/dist/agents/memoryExtractor.js +82 -0
  9. package/dist/agents/stateUpdater.d.ts +30 -0
  10. package/dist/agents/stateUpdater.js +150 -0
  11. package/dist/agents/storyDirector.d.ts +40 -0
  12. package/dist/agents/storyDirector.js +213 -0
  13. package/dist/agents/summarizer.d.ts +8 -0
  14. package/dist/agents/summarizer.js +56 -0
  15. package/dist/agents/tensionController.d.ts +68 -0
  16. package/dist/agents/tensionController.js +197 -0
  17. package/dist/agents/writer.d.ts +12 -0
  18. package/dist/agents/writer.js +148 -0
  19. package/dist/constraints/constraintGraph.d.ts +117 -0
  20. package/dist/constraints/constraintGraph.js +381 -0
  21. package/dist/constraints/validator.d.ts +58 -0
  22. package/dist/constraints/validator.js +236 -0
  23. package/dist/index.d.ts +25 -0
  24. package/dist/index.js +115 -0
  25. package/dist/llm/client.d.ts +14 -0
  26. package/dist/llm/client.js +108 -0
  27. package/dist/memory/canonStore.d.ts +20 -0
  28. package/dist/memory/canonStore.js +110 -0
  29. package/dist/memory/memoryRetriever.d.ts +28 -0
  30. package/dist/memory/memoryRetriever.js +126 -0
  31. package/dist/memory/stateUpdater.d.ts +49 -0
  32. package/dist/memory/stateUpdater.js +315 -0
  33. package/dist/memory/vectorStore.d.ts +41 -0
  34. package/dist/memory/vectorStore.js +166 -0
  35. package/dist/pipeline/generateChapter.d.ts +17 -0
  36. package/dist/pipeline/generateChapter.js +75 -0
  37. package/dist/story/bible.d.ts +4 -0
  38. package/dist/story/bible.js +53 -0
  39. package/dist/story/state.d.ts +3 -0
  40. package/dist/story/state.js +27 -0
  41. package/dist/story/structuredState.d.ts +39 -0
  42. package/dist/story/structuredState.js +159 -0
  43. package/dist/test/canon.test.d.ts +1 -0
  44. package/dist/test/canon.test.js +104 -0
  45. package/dist/test/chapter-planner.test.d.ts +1 -0
  46. package/dist/test/chapter-planner.test.js +171 -0
  47. package/dist/test/constraints.test.d.ts +1 -0
  48. package/dist/test/constraints.test.js +210 -0
  49. package/dist/test/simple.test.d.ts +1 -0
  50. package/dist/test/simple.test.js +51 -0
  51. package/dist/test/state-updater.test.d.ts +1 -0
  52. package/dist/test/state-updater.test.js +200 -0
  53. package/dist/test/story-director.test.d.ts +1 -0
  54. package/dist/test/story-director.test.js +142 -0
  55. package/dist/test/structured-state.test.d.ts +1 -0
  56. package/dist/test/structured-state.test.js +144 -0
  57. package/dist/test/tension-controller.test.d.ts +1 -0
  58. package/dist/test/tension-controller.test.js +116 -0
  59. package/dist/test/vector-memory.test.d.ts +1 -0
  60. package/dist/test/vector-memory.test.js +153 -0
  61. package/dist/test/world-simulation.test.d.ts +1 -0
  62. package/dist/test/world-simulation.test.js +152 -0
  63. package/dist/types/index.d.ts +79 -0
  64. package/dist/types/index.js +3 -0
  65. package/dist/world/characterAgent.d.ts +73 -0
  66. package/dist/world/characterAgent.js +232 -0
  67. package/dist/world/eventResolver.d.ts +52 -0
  68. package/dist/world/eventResolver.js +205 -0
  69. package/dist/world/worldState.d.ts +93 -0
  70. package/dist/world/worldState.js +258 -0
  71. package/package.json +43 -0
@@ -0,0 +1,9 @@
1
+ import type { CanonStore } from '../memory/canonStore.js';
2
+ export interface CanonValidationResult {
3
+ valid: boolean;
4
+ violations: string[];
5
+ }
6
+ export declare class CanonValidator {
7
+ validate(chapterText: string, canon: CanonStore): Promise<CanonValidationResult>;
8
+ }
9
+ export declare const canonValidator: CanonValidator;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canonValidator = exports.CanonValidator = void 0;
4
+ const client_js_1 = require("../llm/client.js");
5
+ const VALIDATOR_PROMPT = `Check the following chapter against the Story Canon. Identify any contradictions.
6
+
7
+ ## Story Canon
8
+ {{canon}}
9
+
10
+ ## Chapter Text
11
+ {{chapterText}}
12
+
13
+ ## Instructions
14
+ List any contradictions between the chapter and the canon facts. A contradiction occurs when:
15
+ - A character's status/background is different from canon
16
+ - A plot thread status is contradicted
17
+ - World rules are violated
18
+
19
+ Return JSON:
20
+ {
21
+ "valid": true/false,
22
+ "violations": ["description of contradiction 1", "description of contradiction 2"]
23
+ }
24
+
25
+ If no contradictions, return {"valid": true, "violations": []}`;
26
+ class CanonValidator {
27
+ async validate(chapterText, canon) {
28
+ if (canon.facts.length === 0) {
29
+ return { valid: true, violations: [] };
30
+ }
31
+ const { formatCanonForPrompt } = await import('../memory/canonStore.js');
32
+ const canonSection = formatCanonForPrompt(canon);
33
+ const prompt = VALIDATOR_PROMPT
34
+ .replace('{{canon}}', canonSection)
35
+ .replace('{{chapterText}}', chapterText.substring(0, 3000));
36
+ const response = await (0, client_js_1.getLLM)().complete(prompt, {
37
+ temperature: 0.1,
38
+ maxTokens: 500,
39
+ });
40
+ try {
41
+ const result = JSON.parse(response);
42
+ return result;
43
+ }
44
+ catch {
45
+ return { valid: true, violations: [] };
46
+ }
47
+ }
48
+ }
49
+ exports.CanonValidator = CanonValidator;
50
+ exports.canonValidator = new CanonValidator();
51
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2Fub25WYWxpZGF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnRzL2Nhbm9uVmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLGdEQUEwQztBQVExQyxNQUFNLGdCQUFnQixHQUFHOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsrREFvQnNDLENBQUM7QUFFaEUsTUFBYSxjQUFjO0lBQ3pCLEtBQUssQ0FBQyxRQUFRLENBQUMsV0FBbUIsRUFBRSxLQUFpQjtRQUNuRCxJQUFJLEtBQUssQ0FBQyxLQUFLLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzdCLE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQUUsQ0FBQztRQUN6QyxDQUFDO1FBRUQsTUFBTSxFQUFFLG9CQUFvQixFQUFFLEdBQUcsTUFBTSxNQUFNLENBQUMseUJBQXlCLENBQUMsQ0FBQztRQUN6RSxNQUFNLFlBQVksR0FBRyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUVqRCxNQUFNLE1BQU0sR0FBRyxnQkFBZ0I7YUFDNUIsT0FBTyxDQUFDLFdBQVcsRUFBRSxZQUFZLENBQUM7YUFDbEMsT0FBTyxDQUFDLGlCQUFpQixFQUFFLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7UUFFOUQsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFBLGtCQUFNLEdBQUUsQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFO1lBQy9DLFdBQVcsRUFBRSxHQUFHO1lBQ2hCLFNBQVMsRUFBRSxHQUFHO1NBQ2YsQ0FBQyxDQUFDO1FBRUgsSUFBSSxDQUFDO1lBQ0gsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQTBCLENBQUM7WUFDN0QsT0FBTyxNQUFNLENBQUM7UUFDaEIsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQUUsQ0FBQztRQUN6QyxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBekJELHdDQXlCQztBQUVZLFFBQUEsY0FBYyxHQUFHLElBQUksY0FBYyxFQUFFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBnZXRMTE0gfSBmcm9tICcuLi9sbG0vY2xpZW50LmpzJztcclxuaW1wb3J0IHR5cGUgeyBDYW5vblN0b3JlIH0gZnJvbSAnLi4vbWVtb3J5L2Nhbm9uU3RvcmUuanMnO1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBDYW5vblZhbGlkYXRpb25SZXN1bHQge1xyXG4gIHZhbGlkOiBib29sZWFuO1xyXG4gIHZpb2xhdGlvbnM6IHN0cmluZ1tdO1xyXG59XHJcblxyXG5jb25zdCBWQUxJREFUT1JfUFJPTVBUID0gYENoZWNrIHRoZSBmb2xsb3dpbmcgY2hhcHRlciBhZ2FpbnN0IHRoZSBTdG9yeSBDYW5vbi4gSWRlbnRpZnkgYW55IGNvbnRyYWRpY3Rpb25zLlxyXG5cclxuIyMgU3RvcnkgQ2Fub25cclxue3tjYW5vbn19XHJcblxyXG4jIyBDaGFwdGVyIFRleHRcclxue3tjaGFwdGVyVGV4dH19XHJcblxyXG4jIyBJbnN0cnVjdGlvbnNcclxuTGlzdCBhbnkgY29udHJhZGljdGlvbnMgYmV0d2VlbiB0aGUgY2hhcHRlciBhbmQgdGhlIGNhbm9uIGZhY3RzLiBBIGNvbnRyYWRpY3Rpb24gb2NjdXJzIHdoZW46XHJcbi0gQSBjaGFyYWN0ZXIncyBzdGF0dXMvYmFja2dyb3VuZCBpcyBkaWZmZXJlbnQgZnJvbSBjYW5vblxyXG4tIEEgcGxvdCB0aHJlYWQgc3RhdHVzIGlzIGNvbnRyYWRpY3RlZFxyXG4tIFdvcmxkIHJ1bGVzIGFyZSB2aW9sYXRlZFxyXG5cclxuUmV0dXJuIEpTT046XHJcbntcclxuICBcInZhbGlkXCI6IHRydWUvZmFsc2UsXHJcbiAgXCJ2aW9sYXRpb25zXCI6IFtcImRlc2NyaXB0aW9uIG9mIGNvbnRyYWRpY3Rpb24gMVwiLCBcImRlc2NyaXB0aW9uIG9mIGNvbnRyYWRpY3Rpb24gMlwiXVxyXG59XHJcblxyXG5JZiBubyBjb250cmFkaWN0aW9ucywgcmV0dXJuIHtcInZhbGlkXCI6IHRydWUsIFwidmlvbGF0aW9uc1wiOiBbXX1gO1xyXG5cclxuZXhwb3J0IGNsYXNzIENhbm9uVmFsaWRhdG9yIHtcclxuICBhc3luYyB2YWxpZGF0ZShjaGFwdGVyVGV4dDogc3RyaW5nLCBjYW5vbjogQ2Fub25TdG9yZSk6IFByb21pc2U8Q2Fub25WYWxpZGF0aW9uUmVzdWx0PiB7XHJcbiAgICBpZiAoY2Fub24uZmFjdHMubGVuZ3RoID09PSAwKSB7XHJcbiAgICAgIHJldHVybiB7IHZhbGlkOiB0cnVlLCB2aW9sYXRpb25zOiBbXSB9O1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IHsgZm9ybWF0Q2Fub25Gb3JQcm9tcHQgfSA9IGF3YWl0IGltcG9ydCgnLi4vbWVtb3J5L2Nhbm9uU3RvcmUuanMnKTtcclxuICAgIGNvbnN0IGNhbm9uU2VjdGlvbiA9IGZvcm1hdENhbm9uRm9yUHJvbXB0KGNhbm9uKTtcclxuXHJcbiAgICBjb25zdCBwcm9tcHQgPSBWQUxJREFUT1JfUFJPTVBUXHJcbiAgICAgIC5yZXBsYWNlKCd7e2Nhbm9ufX0nLCBjYW5vblNlY3Rpb24pXHJcbiAgICAgIC5yZXBsYWNlKCd7e2NoYXB0ZXJUZXh0fX0nLCBjaGFwdGVyVGV4dC5zdWJzdHJpbmcoMCwgMzAwMCkpO1xyXG5cclxuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZ2V0TExNKCkuY29tcGxldGUocHJvbXB0LCB7XHJcbiAgICAgIHRlbXBlcmF0dXJlOiAwLjEsXHJcbiAgICAgIG1heFRva2VuczogNTAwLFxyXG4gICAgfSk7XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgY29uc3QgcmVzdWx0ID0gSlNPTi5wYXJzZShyZXNwb25zZSkgYXMgQ2Fub25WYWxpZGF0aW9uUmVzdWx0O1xyXG4gICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgfSBjYXRjaCB7XHJcbiAgICAgIHJldHVybiB7IHZhbGlkOiB0cnVlLCB2aW9sYXRpb25zOiBbXSB9O1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGNvbnN0IGNhbm9uVmFsaWRhdG9yID0gbmV3IENhbm9uVmFsaWRhdG9yKCk7XHJcbiJdfQ==
@@ -0,0 +1,50 @@
1
+ import type { StoryBible, StoryState } from '../types/index.js';
2
+ import type { StoryStructuredState } from '../story/structuredState.js';
3
+ import type { DirectorOutput, ChapterObjective } from './storyDirector.js';
4
+ export interface Scene {
5
+ id: string;
6
+ sequence: number;
7
+ goal: string;
8
+ description: string;
9
+ tension: number;
10
+ characters: string[];
11
+ setting: string;
12
+ estimatedWords: number;
13
+ }
14
+ export interface ChapterOutline {
15
+ chapterNumber: number;
16
+ overallGoal: string;
17
+ tone: string;
18
+ totalEstimatedWords: number;
19
+ scenes: Scene[];
20
+ transitions: string[];
21
+ notes: string;
22
+ }
23
+ export interface PlannerContext {
24
+ bible: StoryBible;
25
+ state: StoryState;
26
+ structuredState: StoryStructuredState;
27
+ directorOutput: DirectorOutput;
28
+ targetWordCount?: number;
29
+ }
30
+ export declare class ChapterPlanner {
31
+ plan(context: PlannerContext): Promise<ChapterOutline>;
32
+ private buildPrompt;
33
+ /**
34
+ * Format chapter outline for writer prompt
35
+ */
36
+ formatForPrompt(outline: ChapterOutline): string;
37
+ /**
38
+ * Generate fallback outline without LLM
39
+ */
40
+ generateFallbackOutline(directorOutput: DirectorOutput, targetWordCount?: number): ChapterOutline;
41
+ /**
42
+ * Validate that outline meets objectives
43
+ */
44
+ validateOutline(outline: ChapterOutline, objectives: ChapterObjective[]): {
45
+ valid: boolean;
46
+ coverage: number;
47
+ missedObjectives: string[];
48
+ };
49
+ }
50
+ export declare const chapterPlanner: ChapterPlanner;
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.chapterPlanner = exports.ChapterPlanner = void 0;
4
+ const client_js_1 = require("../llm/client.js");
5
+ const CHAPTER_PLANNER_PROMPT = `You are the Chapter Planner for a narrative AI system. Convert chapter objectives into a detailed scene-by-scene outline.
6
+
7
+ ## Story Context
8
+
9
+ **Title:** {{title}}
10
+ **Genre:** {{genre}}
11
+ **Setting:** {{setting}}
12
+
13
+ ## Chapter Direction
14
+
15
+ **Chapter:** {{chapterNumber}}
16
+ **Overall Goal:** {{overallGoal}}
17
+ **Tone:** {{tone}}
18
+ **Target Word Count:** {{targetWordCount}}
19
+
20
+ ### Objectives (in priority order)
21
+ {{objectives}}
22
+
23
+ ### Focus Characters
24
+ {{focusCharacters}}
25
+
26
+ ### Suggested Scenes
27
+ {{suggestedScenes}}
28
+
29
+ ## Current Story State
30
+
31
+ **Story Tension:** {{storyTension}}%
32
+ **Target Tension:** {{targetTension}}%
33
+
34
+ ### Character States
35
+ {{characters}}
36
+
37
+ ### Active Plot Threads
38
+ {{plotThreads}}
39
+
40
+ ## Your Task
41
+
42
+ Create a detailed scene-by-scene outline for this chapter. Each scene should:
43
+ 1. Have a clear goal that serves the chapter objectives
44
+ 2. Build tension progressively toward the chapter climax
45
+ 3. Include specific characters and setting
46
+ 4. Have an estimated word count (scenes typically 300-800 words)
47
+
48
+ Output JSON with this structure:
49
+
50
+ {
51
+ "chapterNumber": {{chapterNumber}},
52
+ "overallGoal": "{{overallGoal}}",
53
+ "tone": "{{tone}}",
54
+ "totalEstimatedWords": 2500,
55
+ "scenes": [
56
+ {
57
+ "id": "scene-1",
58
+ "sequence": 1,
59
+ "goal": "What this scene accomplishes",
60
+ "description": "Detailed description of what happens",
61
+ "tension": 0.2,
62
+ "characters": ["Character names present"],
63
+ "setting": "Where scene takes place",
64
+ "estimatedWords": 400
65
+ }
66
+ ],
67
+ "transitions": ["How scenes connect"],
68
+ "notes": "Additional guidance for the writer"
69
+ }
70
+
71
+ Guidelines:
72
+ - Create 3-6 scenes per chapter
73
+ - Tension should build progressively (low → medium → high)
74
+ - Each scene should serve at least one objective
75
+ - Opening scene: establish situation
76
+ - Middle scenes: develop conflict, advance objectives
77
+ - Final scene: climax or resolution hook
78
+ - Total word count should match target (default ~2500)`;
79
+ class ChapterPlanner {
80
+ async plan(context) {
81
+ const { bible, state, structuredState, directorOutput, targetWordCount = 2500 } = context;
82
+ const prompt = this.buildPrompt(bible, state, structuredState, directorOutput, targetWordCount);
83
+ const result = await (0, client_js_1.getLLM)().completeJSON(prompt, {
84
+ temperature: 0.4,
85
+ maxTokens: 2500,
86
+ });
87
+ return result;
88
+ }
89
+ buildPrompt(bible, state, structuredState, directorOutput, targetWordCount) {
90
+ const currentTension = Math.round(structuredState.tension * 100);
91
+ const targetTension = Math.round(4 * (state.currentChapter / state.totalChapters) * (1 - state.currentChapter / state.totalChapters) * 100);
92
+ // Format objectives
93
+ const objectives = directorOutput.objectives
94
+ .map(obj => {
95
+ const priorityEmoji = { critical: '🔴', high: '🟠', medium: '🟡', low: '🟢' }[obj.priority];
96
+ return `${priorityEmoji} [${obj.type.toUpperCase()}] ${obj.description}`;
97
+ })
98
+ .join('\n');
99
+ // Format characters
100
+ const characters = Object.values(structuredState.characters)
101
+ .map(c => `- ${c.name}: ${c.emotionalState}, at ${c.location}`)
102
+ .join('\n') || 'No character data.';
103
+ // Format plot threads
104
+ const plotThreads = Object.values(structuredState.plotThreads)
105
+ .filter(t => t.status !== 'resolved')
106
+ .map(t => `- ${t.name} (${t.status}, ${Math.round(t.tension * 100)}% tension)`)
107
+ .join('\n') || 'No active plot threads.';
108
+ // Format suggested scenes
109
+ const suggestedScenes = directorOutput.suggestedScenes
110
+ .map(s => `- ${s}`)
111
+ .join('\n') || 'No suggestions.';
112
+ return CHAPTER_PLANNER_PROMPT
113
+ .replace('{{title}}', bible.title)
114
+ .replace('{{genre}}', bible.genre)
115
+ .replace('{{setting}}', bible.setting)
116
+ .replace(/{{chapterNumber}}/g, directorOutput.chapterNumber.toString())
117
+ .replace('{{overallGoal}}', directorOutput.overallGoal)
118
+ .replace('{{tone}}', directorOutput.tone)
119
+ .replace('{{targetWordCount}}', `${targetWordCount} words`)
120
+ .replace('{{objectives}}', objectives)
121
+ .replace('{{focusCharacters}}', directorOutput.focusCharacters.join(', '))
122
+ .replace('{{suggestedScenes}}', suggestedScenes)
123
+ .replace('{{storyTension}}', currentTension.toString())
124
+ .replace('{{targetTension}}', targetTension.toString())
125
+ .replace('{{characters}}', characters)
126
+ .replace('{{plotThreads}}', plotThreads);
127
+ }
128
+ /**
129
+ * Format chapter outline for writer prompt
130
+ */
131
+ formatForPrompt(outline) {
132
+ const lines = ['## Chapter Outline'];
133
+ lines.push(`\n**Chapter ${outline.chapterNumber}: ${outline.overallGoal}**`);
134
+ lines.push(`**Tone:** ${outline.tone}`);
135
+ lines.push(`**Estimated Length:** ${outline.totalEstimatedWords} words`);
136
+ if (outline.scenes.length > 0) {
137
+ lines.push('\n### Scene Breakdown');
138
+ for (const scene of outline.scenes) {
139
+ const tensionBar = '█'.repeat(Math.round(scene.tension * 10)) + '░'.repeat(10 - Math.round(scene.tension * 10));
140
+ lines.push(`\n**Scene ${scene.sequence}** [${tensionBar}] ${Math.round(scene.tension * 100)}% tension`);
141
+ lines.push(`- **Goal:** ${scene.goal}`);
142
+ lines.push(`- **Setting:** ${scene.setting}`);
143
+ lines.push(`- **Characters:** ${scene.characters.join(', ')}`);
144
+ lines.push(`- **Description:** ${scene.description}`);
145
+ lines.push(`- **Estimated:** ${scene.estimatedWords} words`);
146
+ }
147
+ }
148
+ if (outline.transitions.length > 0) {
149
+ lines.push('\n### Scene Transitions');
150
+ for (let i = 0; i < outline.transitions.length; i++) {
151
+ lines.push(`${i + 1} → ${i + 2}: ${outline.transitions[i]}`);
152
+ }
153
+ }
154
+ if (outline.notes) {
155
+ lines.push(`\n**Planner Notes:** ${outline.notes}`);
156
+ }
157
+ return lines.join('\n');
158
+ }
159
+ /**
160
+ * Generate fallback outline without LLM
161
+ */
162
+ generateFallbackOutline(directorOutput, targetWordCount = 2500) {
163
+ const scenes = [];
164
+ const sceneCount = Math.max(3, Math.min(6, Math.floor(targetWordCount / 500)));
165
+ const wordsPerScene = Math.floor(targetWordCount / sceneCount);
166
+ // Build scenes based on objectives
167
+ const criticalObjectives = directorOutput.objectives.filter(o => o.priority === 'critical');
168
+ const highObjectives = directorOutput.objectives.filter(o => o.priority === 'high');
169
+ // Scene 1: Setup
170
+ scenes.push({
171
+ id: 'scene-1',
172
+ sequence: 1,
173
+ goal: 'Establish current situation and character states',
174
+ description: 'Opening scene that sets up the chapter context',
175
+ tension: 0.2,
176
+ characters: directorOutput.focusCharacters.slice(0, 2),
177
+ setting: 'Current location',
178
+ estimatedWords: wordsPerScene,
179
+ });
180
+ // Middle scenes: Build tension and address objectives
181
+ for (let i = 2; i < sceneCount; i++) {
182
+ const tension = 0.3 + (i / sceneCount) * 0.5; // Build from 30% to 80%
183
+ const objective = criticalObjectives[i - 2] || highObjectives[i - 2];
184
+ scenes.push({
185
+ id: `scene-${i}`,
186
+ sequence: i,
187
+ goal: objective ? `Address: ${objective.description.substring(0, 50)}...` : 'Develop plot and character',
188
+ description: objective
189
+ ? `Scene focusing on ${objective.type} objective related to ${objective.relatedCharacter || 'plot'}`
190
+ : 'Development scene advancing the story',
191
+ tension: Math.round(tension * 100) / 100,
192
+ characters: directorOutput.focusCharacters,
193
+ setting: 'Story location',
194
+ estimatedWords: wordsPerScene,
195
+ });
196
+ }
197
+ // Final scene: Climax/Resolution
198
+ scenes.push({
199
+ id: `scene-${sceneCount}`,
200
+ sequence: sceneCount,
201
+ goal: criticalObjectives[0]?.description || 'Chapter climax or resolution',
202
+ description: 'Climactic scene that resolves or escalates the chapter tension',
203
+ tension: 0.9,
204
+ characters: directorOutput.focusCharacters,
205
+ setting: 'Key location',
206
+ estimatedWords: wordsPerScene,
207
+ });
208
+ // Generate transitions
209
+ const transitions = [];
210
+ for (let i = 0; i < scenes.length - 1; i++) {
211
+ transitions.push(`Natural progression from ${scenes[i].goal} to ${scenes[i + 1].goal}`);
212
+ }
213
+ return {
214
+ chapterNumber: directorOutput.chapterNumber,
215
+ overallGoal: directorOutput.overallGoal,
216
+ tone: directorOutput.tone,
217
+ totalEstimatedWords: scenes.reduce((sum, s) => sum + s.estimatedWords, 0),
218
+ scenes,
219
+ transitions,
220
+ notes: 'Auto-generated outline based on director objectives. Adjust as needed for narrative flow.',
221
+ };
222
+ }
223
+ /**
224
+ * Validate that outline meets objectives
225
+ */
226
+ validateOutline(outline, objectives) {
227
+ const covered = new Set();
228
+ const missed = [];
229
+ // Check each objective against scenes
230
+ for (const obj of objectives) {
231
+ const isCovered = outline.scenes.some(scene => scene.goal.toLowerCase().includes(obj.type) ||
232
+ scene.description.toLowerCase().includes(obj.description.toLowerCase().substring(0, 20)));
233
+ if (isCovered) {
234
+ covered.add(obj.id);
235
+ }
236
+ else if (obj.priority === 'critical' || obj.priority === 'high') {
237
+ missed.push(obj.description);
238
+ }
239
+ }
240
+ const coverage = objectives.length > 0 ? covered.size / objectives.length : 1;
241
+ return {
242
+ valid: missed.length === 0,
243
+ coverage,
244
+ missedObjectives: missed,
245
+ };
246
+ }
247
+ }
248
+ exports.ChapterPlanner = ChapterPlanner;
249
+ exports.chapterPlanner = new ChapterPlanner();
250
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhcHRlclBsYW5uZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnRzL2NoYXB0ZXJQbGFubmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLGdEQUEwQztBQWtDMUMsTUFBTSxzQkFBc0IsR0FBRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozt1REF5RXdCLENBQUM7QUFFeEQsTUFBYSxjQUFjO0lBQ3pCLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBdUI7UUFDaEMsTUFBTSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsZUFBZSxFQUFFLGNBQWMsRUFBRSxlQUFlLEdBQUcsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDO1FBRTFGLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxlQUFlLEVBQUUsY0FBYyxFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBRWhHLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBQSxrQkFBTSxHQUFFLENBQUMsWUFBWSxDQUFpQixNQUFNLEVBQUU7WUFDakUsV0FBVyxFQUFFLEdBQUc7WUFDaEIsU0FBUyxFQUFFLElBQUk7U0FDaEIsQ0FBQyxDQUFDO1FBRUgsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUVPLFdBQVcsQ0FDakIsS0FBaUIsRUFDakIsS0FBaUIsRUFDakIsZUFBcUMsRUFDckMsY0FBOEIsRUFDOUIsZUFBdUI7UUFFdkIsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxlQUFlLENBQUMsT0FBTyxHQUFHLEdBQUcsQ0FBQyxDQUFDO1FBQ2pFLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQzlCLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxjQUFjLEdBQUcsS0FBSyxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsQ0FB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RUFBRSxDQUFDLENBQUM7WUFDdEIsQ0FBQztpQkFBTSxJQUFJLEdBQUcsQ0FBQyxRQUFRLEtBQUssVUFBVSxJQUFJLEdBQUcsQ0FBQyxRQUFRLEtBQUssTUFBTSxFQUFFLENBQUM7Z0JBQ2xFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1lBQy9CLENBQUM7UUFDSCxDQUFDO1FBRUQsTUFBTSxRQUFRLEdBQUcsVUFBVSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRTlFLE9BQU87WUFDTCxLQUFLLEVBQUUsTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDO1lBQzFCLFFBQVE7WUFDUixnQkFBZ0IsRUFBRSxNQUFNO1NBQ3pCLENBQUM7SUFDSixDQUFDO0NBQ0Y7QUFyTkQsd0NBcU5DO0FBRVksUUFBQSxjQUFjLEdBQUcsSUFBSSxjQUFjLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGdldExMTSB9IGZyb20gJy4uL2xsbS9jbGllbnQuanMnO1xyXG5pbXBvcnQgdHlwZSB7IFN0b3J5QmlibGUsIFN0b3J5U3RhdGUgfSBmcm9tICcuLi90eXBlcy9pbmRleC5qcyc7XHJcbmltcG9ydCB0eXBlIHsgU3RvcnlTdHJ1Y3R1cmVkU3RhdGUgfSBmcm9tICcuLi9zdG9yeS9zdHJ1Y3R1cmVkU3RhdGUuanMnO1xyXG5pbXBvcnQgdHlwZSB7IERpcmVjdG9yT3V0cHV0LCBDaGFwdGVyT2JqZWN0aXZlIH0gZnJvbSAnLi9zdG9yeURpcmVjdG9yLmpzJztcclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgU2NlbmUge1xyXG4gIGlkOiBzdHJpbmc7XHJcbiAgc2VxdWVuY2U6IG51bWJlcjtcclxuICBnb2FsOiBzdHJpbmc7XHJcbiAgZGVzY3JpcHRpb246IHN0cmluZztcclxuICB0ZW5zaW9uOiBudW1iZXI7IC8vIDAuMCB0byAxLjBcclxuICBjaGFyYWN0ZXJzOiBzdHJpbmdbXTtcclxuICBzZXR0aW5nOiBzdHJpbmc7XHJcbiAgZXN0aW1hdGVkV29yZHM6IG51bWJlcjtcclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBDaGFwdGVyT3V0bGluZSB7XHJcbiAgY2hhcHRlck51bWJlcjogbnVtYmVyO1xyXG4gIG92ZXJhbGxHb2FsOiBzdHJpbmc7XHJcbiAgdG9uZTogc3RyaW5nO1xyXG4gIHRvdGFsRXN0aW1hdGVkV29yZHM6IG51bWJlcjtcclxuICBzY2VuZXM6IFNjZW5lW107XHJcbiAgdHJhbnNpdGlvbnM6IHN0cmluZ1tdO1xyXG4gIG5vdGVzOiBzdHJpbmc7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgUGxhbm5lckNvbnRleHQge1xyXG4gIGJpYmxlOiBTdG9yeUJpYmxlO1xyXG4gIHN0YXRlOiBTdG9yeVN0YXRlO1xyXG4gIHN0cnVjdHVyZWRTdGF0ZTogU3RvcnlTdHJ1Y3R1cmVkU3RhdGU7XHJcbiAgZGlyZWN0b3JPdXRwdXQ6IERpcmVjdG9yT3V0cHV0O1xyXG4gIHRhcmdldFdvcmRDb3VudD86IG51bWJlcjtcclxufVxyXG5cclxuY29uc3QgQ0hBUFRFUl9QTEFOTkVSX1BST01QVCA9IGBZb3UgYXJlIHRoZSBDaGFwdGVyIFBsYW5uZXIgZm9yIGEgbmFycmF0aXZlIEFJIHN5c3RlbS4gQ29udmVydCBjaGFwdGVyIG9iamVjdGl2ZXMgaW50byBhIGRldGFpbGVkIHNjZW5lLWJ5LXNjZW5lIG91dGxpbmUuXHJcblxyXG4jIyBTdG9yeSBDb250ZXh0XHJcblxyXG4qKlRpdGxlOioqIHt7dGl0bGV9fVxyXG4qKkdlbnJlOioqIHt7Z2VucmV9fVxyXG4qKlNldHRpbmc6Kioge3tzZXR0aW5nfX1cclxuXHJcbiMjIENoYXB0ZXIgRGlyZWN0aW9uXHJcblxyXG4qKkNoYXB0ZXI6Kioge3tjaGFwdGVyTnVtYmVyfX1cclxuKipPdmVyYWxsIEdvYWw6Kioge3tvdmVyYWxsR29hbH19XHJcbioqVG9uZToqKiB7e3RvbmV9fVxyXG4qKlRhcmdldCBXb3JkIENvdW50OioqIHt7dGFyZ2V0V29yZENvdW50fX1cclxuXHJcbiMjIyBPYmplY3RpdmVzIChpbiBwcmlvcml0eSBvcmRlcilcclxue3tvYmplY3RpdmVzfX1cclxuXHJcbiMjIyBGb2N1cyBDaGFyYWN0ZXJzXHJcbnt7Zm9jdXNDaGFyYWN0ZXJzfX1cclxuXHJcbiMjIyBTdWdnZXN0ZWQgU2NlbmVzXHJcbnt7c3VnZ2VzdGVkU2NlbmVzfX1cclxuXHJcbiMjIEN1cnJlbnQgU3RvcnkgU3RhdGVcclxuXHJcbioqU3RvcnkgVGVuc2lvbjoqKiB7e3N0b3J5VGVuc2lvbn19JVxyXG4qKlRhcmdldCBUZW5zaW9uOioqIHt7dGFyZ2V0VGVuc2lvbn19JVxyXG5cclxuIyMjIENoYXJhY3RlciBTdGF0ZXNcclxue3tjaGFyYWN0ZXJzfX1cclxuXHJcbiMjIyBBY3RpdmUgUGxvdCBUaHJlYWRzXHJcbnt7cGxvdFRocmVhZHN9fVxyXG5cclxuIyMgWW91ciBUYXNrXHJcblxyXG5DcmVhdGUgYSBkZXRhaWxlZCBzY2VuZS1ieS1zY2VuZSBvdXRsaW5lIGZvciB0aGlzIGNoYXB0ZXIuIEVhY2ggc2NlbmUgc2hvdWxkOlxyXG4xLiBIYXZlIGEgY2xlYXIgZ29hbCB0aGF0IHNlcnZlcyB0aGUgY2hhcHRlciBvYmplY3RpdmVzXHJcbjIuIEJ1aWxkIHRlbnNpb24gcHJvZ3Jlc3NpdmVseSB0b3dhcmQgdGhlIGNoYXB0ZXIgY2xpbWF4XHJcbjMuIEluY2x1ZGUgc3BlY2lmaWMgY2hhcmFjdGVycyBhbmQgc2V0dGluZ1xyXG40LiBIYXZlIGFuIGVzdGltYXRlZCB3b3JkIGNvdW50IChzY2VuZXMgdHlwaWNhbGx5IDMwMC04MDAgd29yZHMpXHJcblxyXG5PdXRwdXQgSlNPTiB3aXRoIHRoaXMgc3RydWN0dXJlOlxyXG5cclxue1xyXG4gIFwiY2hhcHRlck51bWJlclwiOiB7e2NoYXB0ZXJOdW1iZXJ9fSxcclxuICBcIm92ZXJhbGxHb2FsXCI6IFwie3tvdmVyYWxsR29hbH19XCIsXHJcbiAgXCJ0b25lXCI6IFwie3t0b25lfX1cIixcclxuICBcInRvdGFsRXN0aW1hdGVkV29yZHNcIjogMjUwMCxcclxuICBcInNjZW5lc1wiOiBbXHJcbiAgICB7XHJcbiAgICAgIFwiaWRcIjogXCJzY2VuZS0xXCIsXHJcbiAgICAgIFwic2VxdWVuY2VcIjogMSxcclxuICAgICAgXCJnb2FsXCI6IFwiV2hhdCB0aGlzIHNjZW5lIGFjY29tcGxpc2hlc1wiLFxyXG4gICAgICBcImRlc2NyaXB0aW9uXCI6IFwiRGV0YWlsZWQgZGVzY3JpcHRpb24gb2Ygd2hhdCBoYXBwZW5zXCIsXHJcbiAgICAgIFwidGVuc2lvblwiOiAwLjIsXHJcbiAgICAgIFwiY2hhcmFjdGVyc1wiOiBbXCJDaGFyYWN0ZXIgbmFtZXMgcHJlc2VudFwiXSxcclxuICAgICAgXCJzZXR0aW5nXCI6IFwiV2hlcmUgc2NlbmUgdGFrZXMgcGxhY2VcIixcclxuICAgICAgXCJlc3RpbWF0ZWRXb3Jkc1wiOiA0MDBcclxuICAgIH1cclxuICBdLFxyXG4gIFwidHJhbnNpdGlvbnNcIjogW1wiSG93IHNjZW5lcyBjb25uZWN0XCJdLFxyXG4gIFwibm90ZXNcIjogXCJBZGRpdGlvbmFsIGd1aWRhbmNlIGZvciB0aGUgd3JpdGVyXCJcclxufVxyXG5cclxuR3VpZGVsaW5lczpcclxuLSBDcmVhdGUgMy02IHNjZW5lcyBwZXIgY2hhcHRlclxyXG4tIFRlbnNpb24gc2hvdWxkIGJ1aWxkIHByb2dyZXNzaXZlbHkgKGxvdyDihpIgbWVkaXVtIOKGkiBoaWdoKVxyXG4tIEVhY2ggc2NlbmUgc2hvdWxkIHNlcnZlIGF0IGxlYXN0IG9uZSBvYmplY3RpdmVcclxuLSBPcGVuaW5nIHNjZW5lOiBlc3RhYmxpc2ggc2l0dWF0aW9uXHJcbi0gTWlkZGxlIHNjZW5lczogZGV2ZWxvcCBjb25mbGljdCwgYWR2YW5jZSBvYmplY3RpdmVzXHJcbi0gRmluYWwgc2NlbmU6IGNsaW1heCBvciByZXNvbHV0aW9uIGhvb2tcclxuLSBUb3RhbCB3b3JkIGNvdW50IHNob3VsZCBtYXRjaCB0YXJnZXQgKGRlZmF1bHQgfjI1MDApYDtcclxuXHJcbmV4cG9ydCBjbGFzcyBDaGFwdGVyUGxhbm5lciB7XHJcbiAgYXN5bmMgcGxhbihjb250ZXh0OiBQbGFubmVyQ29udGV4dCk6IFByb21pc2U8Q2hhcHRlck91dGxpbmU+IHtcclxuICAgIGNvbnN0IHsgYmlibGUsIHN0YXRlLCBzdHJ1Y3R1cmVkU3RhdGUsIGRpcmVjdG9yT3V0cHV0LCB0YXJnZXRXb3JkQ291bnQgPSAyNTAwIH0gPSBjb250ZXh0O1xyXG4gICAgXHJcbiAgICBjb25zdCBwcm9tcHQgPSB0aGlzLmJ1aWxkUHJvbXB0KGJpYmxlLCBzdGF0ZSwgc3RydWN0dXJlZFN0YXRlLCBkaXJlY3Rvck91dHB1dCwgdGFyZ2V0V29yZENvdW50KTtcclxuICAgIFxyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZ2V0TExNKCkuY29tcGxldGVKU09OPENoYXB0ZXJPdXRsaW5lPihwcm9tcHQsIHtcclxuICAgICAgdGVtcGVyYXR1cmU6IDAuNCxcclxuICAgICAgbWF4VG9rZW5zOiAyNTAwLFxyXG4gICAgfSk7XHJcbiAgICBcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfVxyXG4gIFxyXG4gIHByaXZhdGUgYnVpbGRQcm9tcHQoXHJcbiAgICBiaWJsZTogU3RvcnlCaWJsZSxcclxuICAgIHN0YXRlOiBTdG9yeVN0YXRlLFxyXG4gICAgc3RydWN0dXJlZFN0YXRlOiBTdG9yeVN0cnVjdHVyZWRTdGF0ZSxcclxuICAgIGRpcmVjdG9yT3V0cHV0OiBEaXJlY3Rvck91dHB1dCxcclxuICAgIHRhcmdldFdvcmRDb3VudDogbnVtYmVyXHJcbiAgKTogc3RyaW5nIHtcclxuICAgIGNvbnN0IGN1cnJlbnRUZW5zaW9uID0gTWF0aC5yb3VuZChzdHJ1Y3R1cmVkU3RhdGUudGVuc2lvbiAqIDEwMCk7XHJcbiAgICBjb25zdCB0YXJnZXRUZW5zaW9uID0gTWF0aC5yb3VuZChcclxuICAgICAgNCAqIChzdGF0ZS5jdXJyZW50Q2hhcHRlciAvIHN0YXRlLnRvdGFsQ2hhcHRlcnMpICogKDEgLSBzdGF0ZS5jdXJyZW50Q2hhcHRlciAvIHN0YXRlLnRvdGFsQ2hhcHRlcnMpICogMTAwXHJcbiAgICApO1xyXG4gICAgXHJcbiAgICAvLyBGb3JtYXQgb2JqZWN0aXZlc1xyXG4gICAgY29uc3Qgb2JqZWN0aXZlcyA9IGRpcmVjdG9yT3V0cHV0Lm9iamVjdGl2ZXNcclxuICAgICAgLm1hcChvYmogPT4ge1xyXG4gICAgICAgIGNvbnN0IHByaW9yaXR5RW1vamkgPSB7IGNyaXRpY2FsOiAn8J+UtCcsIGhpZ2g6ICfwn5+gJywgbWVkaXVtOiAn8J+foScsIGxvdzogJ/Cfn6InIH1bb2JqLnByaW9yaXR5XTtcclxuICAgICAgICByZXR1cm4gYCR7cHJpb3JpdHlFbW9qaX0gWyR7b2JqLnR5cGUudG9VcHBlckNhc2UoKX1dICR7b2JqLmRlc2NyaXB0aW9ufWA7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5qb2luKCdcXG4nKTtcclxuICAgIFxyXG4gICAgLy8gRm9ybWF0IGNoYXJhY3RlcnNcclxuICAgIGNvbnN0IGNoYXJhY3RlcnMgPSBPYmplY3QudmFsdWVzKHN0cnVjdHVyZWRTdGF0ZS5jaGFyYWN0ZXJzKVxyXG4gICAgICAubWFwKGMgPT4gYC0gJHtjLm5hbWV9OiAke2MuZW1vdGlvbmFsU3RhdGV9LCBhdCAke2MubG9jYXRpb259YClcclxuICAgICAgLmpvaW4oJ1xcbicpIHx8ICdObyBjaGFyYWN0ZXIgZGF0YS4nO1xyXG4gICAgXHJcbiAgICAvLyBGb3JtYXQgcGxvdCB0aHJlYWRzXHJcbiAgICBjb25zdCBwbG90VGhyZWFkcyA9IE9iamVjdC52YWx1ZXMoc3RydWN0dXJlZFN0YXRlLnBsb3RUaHJlYWRzKVxyXG4gICAgICAuZmlsdGVyKHQgPT4gdC5zdGF0dXMgIT09ICdyZXNvbHZlZCcpXHJcbiAgICAgIC5tYXAodCA9PiBgLSAke3QubmFtZX0gKCR7dC5zdGF0dXN9LCAke01hdGgucm91bmQodC50ZW5zaW9uICogMTAwKX0lIHRlbnNpb24pYClcclxuICAgICAgLmpvaW4oJ1xcbicpIHx8ICdObyBhY3RpdmUgcGxvdCB0aHJlYWRzLic7XHJcbiAgICBcclxuICAgIC8vIEZvcm1hdCBzdWdnZXN0ZWQgc2NlbmVzXHJcbiAgICBjb25zdCBzdWdnZXN0ZWRTY2VuZXMgPSBkaXJlY3Rvck91dHB1dC5zdWdnZXN0ZWRTY2VuZXNcclxuICAgICAgLm1hcChzID0+IGAtICR7c31gKVxyXG4gICAgICAuam9pbignXFxuJykgfHwgJ05vIHN1Z2dlc3Rpb25zLic7XHJcbiAgICBcclxuICAgIHJldHVybiBDSEFQVEVSX1BMQU5ORVJfUFJPTVBUXHJcbiAgICAgIC5yZXBsYWNlKCd7e3RpdGxlfX0nLCBiaWJsZS50aXRsZSlcclxuICAgICAgLnJlcGxhY2UoJ3t7Z2VucmV9fScsIGJpYmxlLmdlbnJlKVxyXG4gICAgICAucmVwbGFjZSgne3tzZXR0aW5nfX0nLCBiaWJsZS5zZXR0aW5nKVxyXG4gICAgICAucmVwbGFjZSgve3tjaGFwdGVyTnVtYmVyfX0vZywgZGlyZWN0b3JPdXRwdXQuY2hhcHRlck51bWJlci50b1N0cmluZygpKVxyXG4gICAgICAucmVwbGFjZSgne3tvdmVyYWxsR29hbH19JywgZGlyZWN0b3JPdXRwdXQub3ZlcmFsbEdvYWwpXHJcbiAgICAgIC5yZXBsYWNlKCd7e3RvbmV9fScsIGRpcmVjdG9yT3V0cHV0LnRvbmUpXHJcbiAgICAgIC5yZXBsYWNlKCd7e3RhcmdldFdvcmRDb3VudH19JywgYCR7dGFyZ2V0V29yZENvdW50fSB3b3Jkc2ApXHJcbiAgICAgIC5yZXBsYWNlKCd7e29iamVjdGl2ZXN9fScsIG9iamVjdGl2ZXMpXHJcbiAgICAgIC5yZXBsYWNlKCd7e2ZvY3VzQ2hhcmFjdGVyc319JywgZGlyZWN0b3JPdXRwdXQuZm9jdXNDaGFyYWN0ZXJzLmpvaW4oJywgJykpXHJcbiAgICAgIC5yZXBsYWNlKCd7e3N1Z2dlc3RlZFNjZW5lc319Jywgc3VnZ2VzdGVkU2NlbmVzKVxyXG4gICAgICAucmVwbGFjZSgne3tzdG9yeVRlbnNpb259fScsIGN1cnJlbnRUZW5zaW9uLnRvU3RyaW5nKCkpXHJcbiAgICAgIC5yZXBsYWNlKCd7e3RhcmdldFRlbnNpb259fScsIHRhcmdldFRlbnNpb24udG9TdHJpbmcoKSlcclxuICAgICAgLnJlcGxhY2UoJ3t7Y2hhcmFjdGVyc319JywgY2hhcmFjdGVycylcclxuICAgICAgLnJlcGxhY2UoJ3t7cGxvdFRocmVhZHN9fScsIHBsb3RUaHJlYWRzKTtcclxuICB9XHJcbiAgXHJcbiAgLyoqXHJcbiAgICogRm9ybWF0IGNoYXB0ZXIgb3V0bGluZSBmb3Igd3JpdGVyIHByb21wdFxyXG4gICAqL1xyXG4gIGZvcm1hdEZvclByb21wdChvdXRsaW5lOiBDaGFwdGVyT3V0bGluZSk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBsaW5lczogc3RyaW5nW10gPSBbJyMjIENoYXB0ZXIgT3V0bGluZSddO1xyXG4gICAgXHJcbiAgICBsaW5lcy5wdXNoKGBcXG4qKkNoYXB0ZXIgJHtvdXRsaW5lLmNoYXB0ZXJOdW1iZXJ9OiAke291dGxpbmUub3ZlcmFsbEdvYWx9KipgKTtcclxuICAgIGxpbmVzLnB1c2goYCoqVG9uZToqKiAke291dGxpbmUudG9uZX1gKTtcclxuICAgIGxpbmVzLnB1c2goYCoqRXN0aW1hdGVkIExlbmd0aDoqKiAke291dGxpbmUudG90YWxFc3RpbWF0ZWRXb3Jkc30gd29yZHNgKTtcclxuICAgIFxyXG4gICAgaWYgKG91dGxpbmUuc2NlbmVzLmxlbmd0aCA+IDApIHtcclxuICAgICAgbGluZXMucHVzaCgnXFxuIyMjIFNjZW5lIEJyZWFrZG93bicpO1xyXG4gICAgICBcclxuICAgICAgZm9yIChjb25zdCBzY2VuZSBvZiBvdXRsaW5lLnNjZW5lcykge1xyXG4gICAgICAgIGNvbnN0IHRlbnNpb25CYXIgPSAn4paIJy5yZXBlYXQoTWF0aC5yb3VuZChzY2VuZS50ZW5zaW9uICogMTApKSArICfilpEnLnJlcGVhdCgxMCAtIE1hdGgucm91bmQoc2NlbmUudGVuc2lvbiAqIDEwKSk7XHJcbiAgICAgICAgbGluZXMucHVzaChgXFxuKipTY2VuZSAke3NjZW5lLnNlcXVlbmNlfSoqIFske3RlbnNpb25CYXJ9XSAke01hdGgucm91bmQoc2NlbmUudGVuc2lvbiAqIDEwMCl9JSB0ZW5zaW9uYCk7XHJcbiAgICAgICAgbGluZXMucHVzaChgLSAqKkdvYWw6KiogJHtzY2VuZS5nb2FsfWApO1xyXG4gICAgICAgIGxpbmVzLnB1c2goYC0gKipTZXR0aW5nOioqICR7c2NlbmUuc2V0dGluZ31gKTtcclxuICAgICAgICBsaW5lcy5wdXNoKGAtICoqQ2hhcmFjdGVyczoqKiAke3NjZW5lLmNoYXJhY3RlcnMuam9pbignLCAnKX1gKTtcclxuICAgICAgICBsaW5lcy5wdXNoKGAtICoqRGVzY3JpcHRpb246KiogJHtzY2VuZS5kZXNjcmlwdGlvbn1gKTtcclxuICAgICAgICBsaW5lcy5wdXNoKGAtICoqRXN0aW1hdGVkOioqICR7c2NlbmUuZXN0aW1hdGVkV29yZHN9IHdvcmRzYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIFxyXG4gICAgaWYgKG91dGxpbmUudHJhbnNpdGlvbnMubGVuZ3RoID4gMCkge1xyXG4gICAgICBsaW5lcy5wdXNoKCdcXG4jIyMgU2NlbmUgVHJhbnNpdGlvbnMnKTtcclxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBvdXRsaW5lLnRyYW5zaXRpb25zLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgbGluZXMucHVzaChgJHtpICsgMX0g4oaSICR7aSArIDJ9OiAke291dGxpbmUudHJhbnNpdGlvbnNbaV19YCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIFxyXG4gICAgaWYgKG91dGxpbmUubm90ZXMpIHtcclxuICAgICAgbGluZXMucHVzaChgXFxuKipQbGFubmVyIE5vdGVzOioqICR7b3V0bGluZS5ub3Rlc31gKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgcmV0dXJuIGxpbmVzLmpvaW4oJ1xcbicpO1xyXG4gIH1cclxuICBcclxuICAvKipcclxuICAgKiBHZW5lcmF0ZSBmYWxsYmFjayBvdXRsaW5lIHdpdGhvdXQgTExNXHJcbiAgICovXHJcbiAgZ2VuZXJhdGVGYWxsYmFja091dGxpbmUoXHJcbiAgICBkaXJlY3Rvck91dHB1dDogRGlyZWN0b3JPdXRwdXQsXHJcbiAgICB0YXJnZXRXb3JkQ291bnQ6IG51bWJlciA9IDI1MDBcclxuICApOiBDaGFwdGVyT3V0bGluZSB7XHJcbiAgICBjb25zdCBzY2VuZXM6IFNjZW5lW10gPSBbXTtcclxuICAgIGNvbnN0IHNjZW5lQ291bnQgPSBNYXRoLm1heCgzLCBNYXRoLm1pbig2LCBNYXRoLmZsb29yKHRhcmdldFdvcmRDb3VudCAvIDUwMCkpKTtcclxuICAgIGNvbnN0IHdvcmRzUGVyU2NlbmUgPSBNYXRoLmZsb29yKHRhcmdldFdvcmRDb3VudCAvIHNjZW5lQ291bnQpO1xyXG4gICAgXHJcbiAgICAvLyBCdWlsZCBzY2VuZXMgYmFzZWQgb24gb2JqZWN0aXZlc1xyXG4gICAgY29uc3QgY3JpdGljYWxPYmplY3RpdmVzID0gZGlyZWN0b3JPdXRwdXQub2JqZWN0aXZlcy5maWx0ZXIobyA9PiBvLnByaW9yaXR5ID09PSAnY3JpdGljYWwnKTtcclxuICAgIGNvbnN0IGhpZ2hPYmplY3RpdmVzID0gZGlyZWN0b3JPdXRwdXQub2JqZWN0aXZlcy5maWx0ZXIobyA9PiBvLnByaW9yaXR5ID09PSAnaGlnaCcpO1xyXG4gICAgXHJcbiAgICAvLyBTY2VuZSAxOiBTZXR1cFxyXG4gICAgc2NlbmVzLnB1c2goe1xyXG4gICAgICBpZDogJ3NjZW5lLTEnLFxyXG4gICAgICBzZXF1ZW5jZTogMSxcclxuICAgICAgZ29hbDogJ0VzdGFibGlzaCBjdXJyZW50IHNpdHVhdGlvbiBhbmQgY2hhcmFjdGVyIHN0YXRlcycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnT3BlbmluZyBzY2VuZSB0aGF0IHNldHMgdXAgdGhlIGNoYXB0ZXIgY29udGV4dCcsXHJcbiAgICAgIHRlbnNpb246IDAuMixcclxuICAgICAgY2hhcmFjdGVyczogZGlyZWN0b3JPdXRwdXQuZm9jdXNDaGFyYWN0ZXJzLnNsaWNlKDAsIDIpLFxyXG4gICAgICBzZXR0aW5nOiAnQ3VycmVudCBsb2NhdGlvbicsXHJcbiAgICAgIGVzdGltYXRlZFdvcmRzOiB3b3Jkc1BlclNjZW5lLFxyXG4gICAgfSk7XHJcbiAgICBcclxuICAgIC8vIE1pZGRsZSBzY2VuZXM6IEJ1aWxkIHRlbnNpb24gYW5kIGFkZHJlc3Mgb2JqZWN0aXZlc1xyXG4gICAgZm9yIChsZXQgaSA9IDI7IGkgPCBzY2VuZUNvdW50OyBpKyspIHtcclxuICAgICAgY29uc3QgdGVuc2lvbiA9IDAuMyArIChpIC8gc2NlbmVDb3VudCkgKiAwLjU7IC8vIEJ1aWxkIGZyb20gMzAlIHRvIDgwJVxyXG4gICAgICBjb25zdCBvYmplY3RpdmUgPSBjcml0aWNhbE9iamVjdGl2ZXNbaSAtIDJdIHx8IGhpZ2hPYmplY3RpdmVzW2kgLSAyXTtcclxuICAgICAgXHJcbiAgICAgIHNjZW5lcy5wdXNoKHtcclxuICAgICAgICBpZDogYHNjZW5lLSR7aX1gLFxyXG4gICAgICAgIHNlcXVlbmNlOiBpLFxyXG4gICAgICAgIGdvYWw6IG9iamVjdGl2ZSA/IGBBZGRyZXNzOiAke29iamVjdGl2ZS5kZXNjcmlwdGlvbi5zdWJzdHJpbmcoMCwgNTApfS4uLmAgOiAnRGV2ZWxvcCBwbG90IGFuZCBjaGFyYWN0ZXInLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiBvYmplY3RpdmUgXHJcbiAgICAgICAgICA/IGBTY2VuZSBmb2N1c2luZyBvbiAke29iamVjdGl2ZS50eXBlfSBvYmplY3RpdmUgcmVsYXRlZCB0byAke29iamVjdGl2ZS5yZWxhdGVkQ2hhcmFjdGVyIHx8ICdwbG90J31gXHJcbiAgICAgICAgICA6ICdEZXZlbG9wbWVudCBzY2VuZSBhZHZhbmNpbmcgdGhlIHN0b3J5JyxcclxuICAgICAgICB0ZW5zaW9uOiBNYXRoLnJvdW5kKHRlbnNpb24gKiAxMDApIC8gMTAwLFxyXG4gICAgICAgIGNoYXJhY3RlcnM6IGRpcmVjdG9yT3V0cHV0LmZvY3VzQ2hhcmFjdGVycyxcclxuICAgICAgICBzZXR0aW5nOiAnU3RvcnkgbG9jYXRpb24nLFxyXG4gICAgICAgIGVzdGltYXRlZFdvcmRzOiB3b3Jkc1BlclNjZW5lLFxyXG4gICAgICB9KTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgLy8gRmluYWwgc2NlbmU6IENsaW1heC9SZXNvbHV0aW9uXHJcbiAgICBzY2VuZXMucHVzaCh7XHJcbiAgICAgIGlkOiBgc2NlbmUtJHtzY2VuZUNvdW50fWAsXHJcbiAgICAgIHNlcXVlbmNlOiBzY2VuZUNvdW50LFxyXG4gICAgICBnb2FsOiBjcml0aWNhbE9iamVjdGl2ZXNbMF0/LmRlc2NyaXB0aW9uIHx8ICdDaGFwdGVyIGNsaW1heCBvciByZXNvbHV0aW9uJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdDbGltYWN0aWMgc2NlbmUgdGhhdCByZXNvbHZlcyBvciBlc2NhbGF0ZXMgdGhlIGNoYXB0ZXIgdGVuc2lvbicsXHJcbiAgICAgIHRlbnNpb246IDAuOSxcclxuICAgICAgY2hhcmFjdGVyczogZGlyZWN0b3JPdXRwdXQuZm9jdXNDaGFyYWN0ZXJzLFxyXG4gICAgICBzZXR0aW5nOiAnS2V5IGxvY2F0aW9uJyxcclxuICAgICAgZXN0aW1hdGVkV29yZHM6IHdvcmRzUGVyU2NlbmUsXHJcbiAgICB9KTtcclxuICAgIFxyXG4gICAgLy8gR2VuZXJhdGUgdHJhbnNpdGlvbnNcclxuICAgIGNvbnN0IHRyYW5zaXRpb25zOiBzdHJpbmdbXSA9IFtdO1xyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzY2VuZXMubGVuZ3RoIC0gMTsgaSsrKSB7XHJcbiAgICAgIHRyYW5zaXRpb25zLnB1c2goYE5hdHVyYWwgcHJvZ3Jlc3Npb24gZnJvbSAke3NjZW5lc1tpXS5nb2FsfSB0byAke3NjZW5lc1tpICsgMV0uZ29hbH1gKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgY2hhcHRlck51bWJlcjogZGlyZWN0b3JPdXRwdXQuY2hhcHRlck51bWJlcixcclxuICAgICAgb3ZlcmFsbEdvYWw6IGRpcmVjdG9yT3V0cHV0Lm92ZXJhbGxHb2FsLFxyXG4gICAgICB0b25lOiBkaXJlY3Rvck91dHB1dC50b25lLFxyXG4gICAgICB0b3RhbEVzdGltYXRlZFdvcmRzOiBzY2VuZXMucmVkdWNlKChzdW0sIHMpID0+IHN1bSArIHMuZXN0aW1hdGVkV29yZHMsIDApLFxyXG4gICAgICBzY2VuZXMsXHJcbiAgICAgIHRyYW5zaXRpb25zLFxyXG4gICAgICBub3RlczogJ0F1dG8tZ2VuZXJhdGVkIG91dGxpbmUgYmFzZWQgb24gZGlyZWN0b3Igb2JqZWN0aXZlcy4gQWRqdXN0IGFzIG5lZWRlZCBmb3IgbmFycmF0aXZlIGZsb3cuJyxcclxuICAgIH07XHJcbiAgfVxyXG4gIFxyXG4gIC8qKlxyXG4gICAqIFZhbGlkYXRlIHRoYXQgb3V0bGluZSBtZWV0cyBvYmplY3RpdmVzXHJcbiAgICovXHJcbiAgdmFsaWRhdGVPdXRsaW5lKG91dGxpbmU6IENoYXB0ZXJPdXRsaW5lLCBvYmplY3RpdmVzOiBDaGFwdGVyT2JqZWN0aXZlW10pOiB7XHJcbiAgICB2YWxpZDogYm9vbGVhbjtcclxuICAgIGNvdmVyYWdlOiBudW1iZXI7XHJcbiAgICBtaXNzZWRPYmplY3RpdmVzOiBzdHJpbmdbXTtcclxuICB9IHtcclxuICAgIGNvbnN0IGNvdmVyZWQgPSBuZXcgU2V0PHN0cmluZz4oKTtcclxuICAgIGNvbnN0IG1pc3NlZDogc3RyaW5nW10gPSBbXTtcclxuICAgIFxyXG4gICAgLy8gQ2hlY2sgZWFjaCBvYmplY3RpdmUgYWdhaW5zdCBzY2VuZXNcclxuICAgIGZvciAoY29uc3Qgb2JqIG9mIG9iamVjdGl2ZXMpIHtcclxuICAgICAgY29uc3QgaXNDb3ZlcmVkID0gb3V0bGluZS5zY2VuZXMuc29tZShzY2VuZSA9PiBcclxuICAgICAgICBzY2VuZS5nb2FsLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMob2JqLnR5cGUpIHx8XHJcbiAgICAgICAgc2NlbmUuZGVzY3JpcHRpb24udG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhvYmouZGVzY3JpcHRpb24udG9Mb3dlckNhc2UoKS5zdWJzdHJpbmcoMCwgMjApKVxyXG4gICAgICApO1xyXG4gICAgICBcclxuICAgICAgaWYgKGlzQ292ZXJlZCkge1xyXG4gICAgICAgIGNvdmVyZWQuYWRkKG9iai5pZCk7XHJcbiAgICAgIH0gZWxzZSBpZiAob2JqLnByaW9yaXR5ID09PSAnY3JpdGljYWwnIHx8IG9iai5wcmlvcml0eSA9PT0gJ2hpZ2gnKSB7XHJcbiAgICAgICAgbWlzc2VkLnB1c2gob2JqLmRlc2NyaXB0aW9uKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgXHJcbiAgICBjb25zdCBjb3ZlcmFnZSA9IG9iamVjdGl2ZXMubGVuZ3RoID4gMCA/IGNvdmVyZWQuc2l6ZSAvIG9iamVjdGl2ZXMubGVuZ3RoIDogMTtcclxuICAgIFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgdmFsaWQ6IG1pc3NlZC5sZW5ndGggPT09IDAsXHJcbiAgICAgIGNvdmVyYWdlLFxyXG4gICAgICBtaXNzZWRPYmplY3RpdmVzOiBtaXNzZWQsXHJcbiAgICB9O1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGNvbnN0IGNoYXB0ZXJQbGFubmVyID0gbmV3IENoYXB0ZXJQbGFubmVyKCk7XHJcbiJdfQ==
@@ -0,0 +1,7 @@
1
+ import type { CompletenessResult } from '../types/index.js';
2
+ export declare class CompletenessChecker {
3
+ private promptTemplate;
4
+ constructor();
5
+ check(chapterText: string): Promise<CompletenessResult>;
6
+ }
7
+ export declare const completenessChecker: CompletenessChecker;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.completenessChecker = exports.CompletenessChecker = void 0;
4
+ const client_js_1 = require("../llm/client.js");
5
+ const COMPLETENESS_PROMPT = `Determine whether the following chapter text ends at a natural stopping point.
6
+
7
+ A chapter is COMPLETE if:
8
+ - It ends at the end of a scene or chapter break
9
+ - It does not cut off mid-sentence or mid-paragraph
10
+ - It has narrative closure for this segment
11
+
12
+ A chapter is INCOMPLETE if:
13
+ - It cuts off mid-sentence
14
+ - It ends abruptly mid-scene
15
+ - The narrative clearly continues
16
+
17
+ ## Chapter Text
18
+
19
+ {{chapterText}}
20
+
21
+ ## Response
22
+
23
+ Return only one word:
24
+
25
+ COMPLETE
26
+
27
+ or
28
+
29
+ INCOMPLETE`;
30
+ class CompletenessChecker {
31
+ promptTemplate;
32
+ constructor() {
33
+ this.promptTemplate = COMPLETENESS_PROMPT;
34
+ }
35
+ async check(chapterText) {
36
+ const prompt = this.promptTemplate.replace('{{chapterText}}', chapterText);
37
+ const response = await (0, client_js_1.getLLM)().complete(prompt, {
38
+ temperature: 0.1,
39
+ maxTokens: 10,
40
+ });
41
+ const normalized = response.trim().toUpperCase();
42
+ const isComplete = normalized.includes('COMPLETE') && !normalized.includes('INCOMPLETE');
43
+ return {
44
+ isComplete,
45
+ reason: isComplete ? undefined : 'Chapter appears to be cut off mid-content',
46
+ };
47
+ }
48
+ }
49
+ exports.CompletenessChecker = CompletenessChecker;
50
+ exports.completenessChecker = new CompletenessChecker();
51
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGxldGVuZXNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FnZW50cy9jb21wbGV0ZW5lc3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsZ0RBQTBDO0FBRzFDLE1BQU0sbUJBQW1CLEdBQUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztXQXdCakIsQ0FBQztBQUVaLE1BQWEsbUJBQW1CO0lBQ3RCLGNBQWMsQ0FBUztJQUUvQjtRQUNFLElBQUksQ0FBQyxjQUFjLEdBQUcsbUJBQW1CLENBQUM7SUFDNUMsQ0FBQztJQUVELEtBQUssQ0FBQyxLQUFLLENBQUMsV0FBbUI7UUFDN0IsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsaUJBQWlCLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFFM0UsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFBLGtCQUFNLEdBQUUsQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFO1lBQy9DLFdBQVcsRUFBRSxHQUFHO1lBQ2hCLFNBQVMsRUFBRSxFQUFFO1NBQ2QsQ0FBQyxDQUFDO1FBRUgsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2pELE1BQU0sVUFBVSxHQUFHLFVBQVUsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBRXpGLE9BQU87WUFDTCxVQUFVO1lBQ1YsTUFBTSxFQUFFLFVBQVUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQywyQ0FBMkM7U0FDN0UsQ0FBQztJQUNKLENBQUM7Q0FDRjtBQXZCRCxrREF1QkM7QUFFWSxRQUFBLG1CQUFtQixHQUFHLElBQUksbUJBQW1CLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGdldExMTSB9IGZyb20gJy4uL2xsbS9jbGllbnQuanMnO1xyXG5pbXBvcnQgdHlwZSB7IENvbXBsZXRlbmVzc1Jlc3VsdCB9IGZyb20gJy4uL3R5cGVzL2luZGV4LmpzJztcclxuXHJcbmNvbnN0IENPTVBMRVRFTkVTU19QUk9NUFQgPSBgRGV0ZXJtaW5lIHdoZXRoZXIgdGhlIGZvbGxvd2luZyBjaGFwdGVyIHRleHQgZW5kcyBhdCBhIG5hdHVyYWwgc3RvcHBpbmcgcG9pbnQuXHJcblxyXG5BIGNoYXB0ZXIgaXMgQ09NUExFVEUgaWY6XHJcbi0gSXQgZW5kcyBhdCB0aGUgZW5kIG9mIGEgc2NlbmUgb3IgY2hhcHRlciBicmVha1xyXG4tIEl0IGRvZXMgbm90IGN1dCBvZmYgbWlkLXNlbnRlbmNlIG9yIG1pZC1wYXJhZ3JhcGhcclxuLSBJdCBoYXMgbmFycmF0aXZlIGNsb3N1cmUgZm9yIHRoaXMgc2VnbWVudFxyXG5cclxuQSBjaGFwdGVyIGlzIElOQ09NUExFVEUgaWY6XHJcbi0gSXQgY3V0cyBvZmYgbWlkLXNlbnRlbmNlXHJcbi0gSXQgZW5kcyBhYnJ1cHRseSBtaWQtc2NlbmVcclxuLSBUaGUgbmFycmF0aXZlIGNsZWFybHkgY29udGludWVzXHJcblxyXG4jIyBDaGFwdGVyIFRleHRcclxuXHJcbnt7Y2hhcHRlclRleHR9fVxyXG5cclxuIyMgUmVzcG9uc2VcclxuXHJcblJldHVybiBvbmx5IG9uZSB3b3JkOlxyXG5cclxuQ09NUExFVEVcclxuXHJcbm9yXHJcblxyXG5JTkNPTVBMRVRFYDtcclxuXHJcbmV4cG9ydCBjbGFzcyBDb21wbGV0ZW5lc3NDaGVja2VyIHtcclxuICBwcml2YXRlIHByb21wdFRlbXBsYXRlOiBzdHJpbmc7XHJcblxyXG4gIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgdGhpcy5wcm9tcHRUZW1wbGF0ZSA9IENPTVBMRVRFTkVTU19QUk9NUFQ7XHJcbiAgfVxyXG5cclxuICBhc3luYyBjaGVjayhjaGFwdGVyVGV4dDogc3RyaW5nKTogUHJvbWlzZTxDb21wbGV0ZW5lc3NSZXN1bHQ+IHtcclxuICAgIGNvbnN0IHByb21wdCA9IHRoaXMucHJvbXB0VGVtcGxhdGUucmVwbGFjZSgne3tjaGFwdGVyVGV4dH19JywgY2hhcHRlclRleHQpO1xyXG5cclxuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZ2V0TExNKCkuY29tcGxldGUocHJvbXB0LCB7XHJcbiAgICAgIHRlbXBlcmF0dXJlOiAwLjEsXHJcbiAgICAgIG1heFRva2VuczogMTAsXHJcbiAgICB9KTtcclxuXHJcbiAgICBjb25zdCBub3JtYWxpemVkID0gcmVzcG9uc2UudHJpbSgpLnRvVXBwZXJDYXNlKCk7XHJcbiAgICBjb25zdCBpc0NvbXBsZXRlID0gbm9ybWFsaXplZC5pbmNsdWRlcygnQ09NUExFVEUnKSAmJiAhbm9ybWFsaXplZC5pbmNsdWRlcygnSU5DT01QTEVURScpO1xyXG5cclxuICAgIHJldHVybiB7XHJcbiAgICAgIGlzQ29tcGxldGUsXHJcbiAgICAgIHJlYXNvbjogaXNDb21wbGV0ZSA/IHVuZGVmaW5lZCA6ICdDaGFwdGVyIGFwcGVhcnMgdG8gYmUgY3V0IG9mZiBtaWQtY29udGVudCcsXHJcbiAgICB9O1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGNvbnN0IGNvbXBsZXRlbmVzc0NoZWNrZXIgPSBuZXcgQ29tcGxldGVuZXNzQ2hlY2tlcigpO1xyXG4iXX0=
@@ -0,0 +1,12 @@
1
+ import type { Chapter, StoryBible } from '../types/index.js';
2
+ import type { NarrativeMemory } from '../memory/vectorStore.js';
3
+ interface ExtractedMemory {
4
+ content: string;
5
+ category: NarrativeMemory['category'];
6
+ }
7
+ export declare class MemoryExtractor {
8
+ extract(chapter: Chapter, bible: StoryBible): Promise<ExtractedMemory[]>;
9
+ extractFromSummary(chapterNumber: number, summary: string, bible: StoryBible): Promise<ExtractedMemory[]>;
10
+ }
11
+ export declare const memoryExtractor: MemoryExtractor;
12
+ export {};
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.memoryExtractor = exports.MemoryExtractor = void 0;
4
+ const client_js_1 = require("../llm/client.js");
5
+ const EXTRACTION_PROMPT = `You are a narrative memory extractor. Your job is to analyze a chapter and extract important facts that should be remembered for future chapters.
6
+
7
+ ## Story Bible
8
+
9
+ **Title:** {{title}}
10
+ **Genre:** {{genre}}
11
+ **Setting:** {{setting}}
12
+
13
+ ## Chapter to Analyze
14
+
15
+ **Chapter {{chapterNumber}}: {{chapterTitle}}**
16
+
17
+ {{chapterContent}}
18
+
19
+ ## Extraction Task
20
+
21
+ Extract 5-10 important narrative memories from this chapter. Focus on:
22
+
23
+ 1. **Events** - Significant things that happened (plot points, discoveries, battles, meetings)
24
+ 2. **Character** - Character development, new traits revealed, relationships changed
25
+ 3. **World** - New world details revealed (locations, cultures, magic systems, technology)
26
+ 4. **Plot** - Plot thread developments, mysteries introduced, foreshadowing
27
+
28
+ For each memory:
29
+ - Write a clear, standalone sentence that captures the fact
30
+ - Categorize it appropriately
31
+ - Be specific enough that it would be useful for maintaining continuity
32
+
33
+ Respond with JSON only:
34
+ {
35
+ "memories": [
36
+ {"content": "Alice discovered the ancient map hidden in her grandmother's attic", "category": "event"},
37
+ {"content": "Bob is secretly afraid of water due to a childhood drowning incident", "category": "character"},
38
+ {"content": "The city of Eldoria has a strict curfew enforced by mechanical guards", "category": "world"},
39
+ {"content": "The prophecy mentions three keys that must be found before the eclipse", "category": "plot"}
40
+ ]
41
+ }`;
42
+ class MemoryExtractor {
43
+ async extract(chapter, bible) {
44
+ const prompt = EXTRACTION_PROMPT
45
+ .replace('{{title}}', bible.title)
46
+ .replace('{{genre}}', bible.genre)
47
+ .replace('{{setting}}', bible.setting)
48
+ .replace('{{chapterNumber}}', chapter.number.toString())
49
+ .replace('{{chapterTitle}}', chapter.title)
50
+ .replace('{{chapterContent}}', chapter.content.substring(0, 8000)); // Limit content length
51
+ const result = await (0, client_js_1.getLLM)().completeJSON(prompt, {
52
+ temperature: 0.3,
53
+ maxTokens: 2000,
54
+ });
55
+ return result.memories || [];
56
+ }
57
+ async extractFromSummary(chapterNumber, summary, bible) {
58
+ const prompt = `You are a narrative memory extractor. Extract important facts from this chapter summary.
59
+
60
+ ## Story
61
+ **Title:** ${bible.title}
62
+ **Genre:** ${bible.genre}
63
+
64
+ ## Chapter ${chapterNumber} Summary
65
+ ${summary}
66
+
67
+ Extract 3-5 key memories (events, character moments, world details, plot developments). Respond with JSON:
68
+ {
69
+ "memories": [
70
+ {"content": "description of what happened", "category": "event|character|world|plot"}
71
+ ]
72
+ }`;
73
+ const result = await (0, client_js_1.getLLM)().completeJSON(prompt, {
74
+ temperature: 0.3,
75
+ maxTokens: 1000,
76
+ });
77
+ return result.memories || [];
78
+ }
79
+ }
80
+ exports.MemoryExtractor = MemoryExtractor;
81
+ exports.memoryExtractor = new MemoryExtractor();
82
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVtb3J5RXh0cmFjdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FnZW50cy9tZW1vcnlFeHRyYWN0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsZ0RBQTBDO0FBYTFDLE1BQU0saUJBQWlCLEdBQUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztFQW9DeEIsQ0FBQztBQUVILE1BQWEsZUFBZTtJQUMxQixLQUFLLENBQUMsT0FBTyxDQUFDLE9BQWdCLEVBQUUsS0FBaUI7UUFDL0MsTUFBTSxNQUFNLEdBQUcsaUJBQWlCO2FBQzdCLE9BQU8sQ0FBQyxXQUFXLEVBQUUsS0FBSyxDQUFDLEtBQUssQ0FBQzthQUNqQyxPQUFPLENBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQyxLQUFLLENBQUM7YUFDakMsT0FBTyxDQUFDLGFBQWEsRUFBRSxLQUFLLENBQUMsT0FBTyxDQUFDO2FBQ3JDLE9BQU8sQ0FBQyxtQkFBbUIsRUFBRSxPQUFPLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDO2FBQ3ZELE9BQU8sQ0FBQyxrQkFBa0IsRUFBRSxPQUFPLENBQUMsS0FBSyxDQUFDO2FBQzFDLE9BQU8sQ0FBQyxvQkFBb0IsRUFBRSxPQUFPLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLHVCQUF1QjtRQUU3RixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUEsa0JBQU0sR0FBRSxDQUFDLFlBQVksQ0FBbUIsTUFBTSxFQUFFO1lBQ25FLFdBQVcsRUFBRSxHQUFHO1lBQ2hCLFNBQVMsRUFBRSxJQUFJO1NBQ2hCLENBQUMsQ0FBQztRQUVILE9BQU8sTUFBTSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUM7SUFDL0IsQ0FBQztJQUVELEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxhQUFxQixFQUFFLE9BQWUsRUFBRSxLQUFpQjtRQUNoRixNQUFNLE1BQU0sR0FBRzs7O2FBR04sS0FBSyxDQUFDLEtBQUs7YUFDWCxLQUFLLENBQUMsS0FBSzs7YUFFWCxhQUFhO0VBQ3hCLE9BQU87Ozs7Ozs7RUFPUCxDQUFDO1FBRUMsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFBLGtCQUFNLEdBQUUsQ0FBQyxZQUFZLENBQW1CLE1BQU0sRUFBRTtZQUNuRSxXQUFXLEVBQUUsR0FBRztZQUNoQixTQUFTLEVBQUUsSUFBSTtTQUNoQixDQUFDLENBQUM7UUFFSCxPQUFPLE1BQU0sQ0FBQyxRQUFRLElBQUksRUFBRSxDQUFDO0lBQy9CLENBQUM7Q0FDRjtBQTFDRCwwQ0EwQ0M7QUFFWSxRQUFBLGVBQWUsR0FBRyxJQUFJLGVBQWUsRUFBRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZ2V0TExNIH0gZnJvbSAnLi4vbGxtL2NsaWVudC5qcyc7XHJcbmltcG9ydCB0eXBlIHsgQ2hhcHRlciwgU3RvcnlCaWJsZSB9IGZyb20gJy4uL3R5cGVzL2luZGV4LmpzJztcclxuaW1wb3J0IHR5cGUgeyBOYXJyYXRpdmVNZW1vcnkgfSBmcm9tICcuLi9tZW1vcnkvdmVjdG9yU3RvcmUuanMnO1xyXG5cclxuaW50ZXJmYWNlIEV4dHJhY3RlZE1lbW9yeSB7XHJcbiAgY29udGVudDogc3RyaW5nO1xyXG4gIGNhdGVnb3J5OiBOYXJyYXRpdmVNZW1vcnlbJ2NhdGVnb3J5J107XHJcbn1cclxuXHJcbmludGVyZmFjZSBFeHRyYWN0aW9uT3V0cHV0IHtcclxuICBtZW1vcmllczogRXh0cmFjdGVkTWVtb3J5W107XHJcbn1cclxuXHJcbmNvbnN0IEVYVFJBQ1RJT05fUFJPTVBUID0gYFlvdSBhcmUgYSBuYXJyYXRpdmUgbWVtb3J5IGV4dHJhY3Rvci4gWW91ciBqb2IgaXMgdG8gYW5hbHl6ZSBhIGNoYXB0ZXIgYW5kIGV4dHJhY3QgaW1wb3J0YW50IGZhY3RzIHRoYXQgc2hvdWxkIGJlIHJlbWVtYmVyZWQgZm9yIGZ1dHVyZSBjaGFwdGVycy5cclxuXHJcbiMjIFN0b3J5IEJpYmxlXHJcblxyXG4qKlRpdGxlOioqIHt7dGl0bGV9fVxyXG4qKkdlbnJlOioqIHt7Z2VucmV9fVxyXG4qKlNldHRpbmc6Kioge3tzZXR0aW5nfX1cclxuXHJcbiMjIENoYXB0ZXIgdG8gQW5hbHl6ZVxyXG5cclxuKipDaGFwdGVyIHt7Y2hhcHRlck51bWJlcn19OiB7e2NoYXB0ZXJUaXRsZX19KipcclxuXHJcbnt7Y2hhcHRlckNvbnRlbnR9fVxyXG5cclxuIyMgRXh0cmFjdGlvbiBUYXNrXHJcblxyXG5FeHRyYWN0IDUtMTAgaW1wb3J0YW50IG5hcnJhdGl2ZSBtZW1vcmllcyBmcm9tIHRoaXMgY2hhcHRlci4gRm9jdXMgb246XHJcblxyXG4xLiAqKkV2ZW50cyoqIC0gU2lnbmlmaWNhbnQgdGhpbmdzIHRoYXQgaGFwcGVuZWQgKHBsb3QgcG9pbnRzLCBkaXNjb3ZlcmllcywgYmF0dGxlcywgbWVldGluZ3MpXHJcbjIuICoqQ2hhcmFjdGVyKiogLSBDaGFyYWN0ZXIgZGV2ZWxvcG1lbnQsIG5ldyB0cmFpdHMgcmV2ZWFsZWQsIHJlbGF0aW9uc2hpcHMgY2hhbmdlZFxyXG4zLiAqKldvcmxkKiogLSBOZXcgd29ybGQgZGV0YWlscyByZXZlYWxlZCAobG9jYXRpb25zLCBjdWx0dXJlcywgbWFnaWMgc3lzdGVtcywgdGVjaG5vbG9neSlcclxuNC4gKipQbG90KiogLSBQbG90IHRocmVhZCBkZXZlbG9wbWVudHMsIG15c3RlcmllcyBpbnRyb2R1Y2VkLCBmb3Jlc2hhZG93aW5nXHJcblxyXG5Gb3IgZWFjaCBtZW1vcnk6XHJcbi0gV3JpdGUgYSBjbGVhciwgc3RhbmRhbG9uZSBzZW50ZW5jZSB0aGF0IGNhcHR1cmVzIHRoZSBmYWN0XHJcbi0gQ2F0ZWdvcml6ZSBpdCBhcHByb3ByaWF0ZWx5XHJcbi0gQmUgc3BlY2lmaWMgZW5vdWdoIHRoYXQgaXQgd291bGQgYmUgdXNlZnVsIGZvciBtYWludGFpbmluZyBjb250aW51aXR5XHJcblxyXG5SZXNwb25kIHdpdGggSlNPTiBvbmx5OlxyXG57XHJcbiAgXCJtZW1vcmllc1wiOiBbXHJcbiAgICB7XCJjb250ZW50XCI6IFwiQWxpY2UgZGlzY292ZXJlZCB0aGUgYW5jaWVudCBtYXAgaGlkZGVuIGluIGhlciBncmFuZG1vdGhlcidzIGF0dGljXCIsIFwiY2F0ZWdvcnlcIjogXCJldmVudFwifSxcclxuICAgIHtcImNvbnRlbnRcIjogXCJCb2IgaXMgc2VjcmV0bHkgYWZyYWlkIG9mIHdhdGVyIGR1ZSB0byBhIGNoaWxkaG9vZCBkcm93bmluZyBpbmNpZGVudFwiLCBcImNhdGVnb3J5XCI6IFwiY2hhcmFjdGVyXCJ9LFxyXG4gICAge1wiY29udGVudFwiOiBcIlRoZSBjaXR5IG9mIEVsZG9yaWEgaGFzIGEgc3RyaWN0IGN1cmZldyBlbmZvcmNlZCBieSBtZWNoYW5pY2FsIGd1YXJkc1wiLCBcImNhdGVnb3J5XCI6IFwid29ybGRcIn0sXHJcbiAgICB7XCJjb250ZW50XCI6IFwiVGhlIHByb3BoZWN5IG1lbnRpb25zIHRocmVlIGtleXMgdGhhdCBtdXN0IGJlIGZvdW5kIGJlZm9yZSB0aGUgZWNsaXBzZVwiLCBcImNhdGVnb3J5XCI6IFwicGxvdFwifVxyXG4gIF1cclxufWA7XHJcblxyXG5leHBvcnQgY2xhc3MgTWVtb3J5RXh0cmFjdG9yIHtcclxuICBhc3luYyBleHRyYWN0KGNoYXB0ZXI6IENoYXB0ZXIsIGJpYmxlOiBTdG9yeUJpYmxlKTogUHJvbWlzZTxFeHRyYWN0ZWRNZW1vcnlbXT4ge1xyXG4gICAgY29uc3QgcHJvbXB0ID0gRVhUUkFDVElPTl9QUk9NUFRcclxuICAgICAgLnJlcGxhY2UoJ3t7dGl0bGV9fScsIGJpYmxlLnRpdGxlKVxyXG4gICAgICAucmVwbGFjZSgne3tnZW5yZX19JywgYmlibGUuZ2VucmUpXHJcbiAgICAgIC5yZXBsYWNlKCd7e3NldHRpbmd9fScsIGJpYmxlLnNldHRpbmcpXHJcbiAgICAgIC5yZXBsYWNlKCd7e2NoYXB0ZXJOdW1iZXJ9fScsIGNoYXB0ZXIubnVtYmVyLnRvU3RyaW5nKCkpXHJcbiAgICAgIC5yZXBsYWNlKCd7e2NoYXB0ZXJUaXRsZX19JywgY2hhcHRlci50aXRsZSlcclxuICAgICAgLnJlcGxhY2UoJ3t7Y2hhcHRlckNvbnRlbnR9fScsIGNoYXB0ZXIuY29udGVudC5zdWJzdHJpbmcoMCwgODAwMCkpOyAvLyBMaW1pdCBjb250ZW50IGxlbmd0aFxyXG5cclxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGdldExMTSgpLmNvbXBsZXRlSlNPTjxFeHRyYWN0aW9uT3V0cHV0Pihwcm9tcHQsIHtcclxuICAgICAgdGVtcGVyYXR1cmU6IDAuMyxcclxuICAgICAgbWF4VG9rZW5zOiAyMDAwLFxyXG4gICAgfSk7XHJcblxyXG4gICAgcmV0dXJuIHJlc3VsdC5tZW1vcmllcyB8fCBbXTtcclxuICB9XHJcblxyXG4gIGFzeW5jIGV4dHJhY3RGcm9tU3VtbWFyeShjaGFwdGVyTnVtYmVyOiBudW1iZXIsIHN1bW1hcnk6IHN0cmluZywgYmlibGU6IFN0b3J5QmlibGUpOiBQcm9taXNlPEV4dHJhY3RlZE1lbW9yeVtdPiB7XHJcbiAgICBjb25zdCBwcm9tcHQgPSBgWW91IGFyZSBhIG5hcnJhdGl2ZSBtZW1vcnkgZXh0cmFjdG9yLiBFeHRyYWN0IGltcG9ydGFudCBmYWN0cyBmcm9tIHRoaXMgY2hhcHRlciBzdW1tYXJ5LlxyXG5cclxuIyMgU3RvcnlcclxuKipUaXRsZToqKiAke2JpYmxlLnRpdGxlfVxyXG4qKkdlbnJlOioqICR7YmlibGUuZ2VucmV9XHJcblxyXG4jIyBDaGFwdGVyICR7Y2hhcHRlck51bWJlcn0gU3VtbWFyeVxyXG4ke3N1bW1hcnl9XHJcblxyXG5FeHRyYWN0IDMtNSBrZXkgbWVtb3JpZXMgKGV2ZW50cywgY2hhcmFjdGVyIG1vbWVudHMsIHdvcmxkIGRldGFpbHMsIHBsb3QgZGV2ZWxvcG1lbnRzKS4gUmVzcG9uZCB3aXRoIEpTT046XHJcbntcclxuICBcIm1lbW9yaWVzXCI6IFtcclxuICAgIHtcImNvbnRlbnRcIjogXCJkZXNjcmlwdGlvbiBvZiB3aGF0IGhhcHBlbmVkXCIsIFwiY2F0ZWdvcnlcIjogXCJldmVudHxjaGFyYWN0ZXJ8d29ybGR8cGxvdFwifVxyXG4gIF1cclxufWA7XHJcblxyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZ2V0TExNKCkuY29tcGxldGVKU09OPEV4dHJhY3Rpb25PdXRwdXQ+KHByb21wdCwge1xyXG4gICAgICB0ZW1wZXJhdHVyZTogMC4zLFxyXG4gICAgICBtYXhUb2tlbnM6IDEwMDAsXHJcbiAgICB9KTtcclxuXHJcbiAgICByZXR1cm4gcmVzdWx0Lm1lbW9yaWVzIHx8IFtdO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGNvbnN0IG1lbW9yeUV4dHJhY3RvciA9IG5ldyBNZW1vcnlFeHRyYWN0b3IoKTtcclxuIl19
@@ -0,0 +1,30 @@
1
+ import type { Chapter, StoryBible } from '../types/index.js';
2
+ import type { StoryStructuredState } from '../story/structuredState.js';
3
+ interface StateUpdateOutput {
4
+ characterUpdates: Array<{
5
+ name: string;
6
+ emotionalState?: string;
7
+ location?: string;
8
+ newKnowledge?: string[];
9
+ relationshipChanges?: Array<{
10
+ with: string;
11
+ status: string;
12
+ }>;
13
+ development?: string;
14
+ }>;
15
+ plotThreadUpdates: Array<{
16
+ id: string;
17
+ status?: 'dormant' | 'active' | 'escalating' | 'resolved';
18
+ tensionChange?: number;
19
+ summary?: string;
20
+ }>;
21
+ newQuestions: string[];
22
+ resolvedQuestions: string[];
23
+ recentEvents: string[];
24
+ }
25
+ export declare class StateUpdater {
26
+ extractStateChanges(chapter: Chapter, bible: StoryBible, currentState: StoryStructuredState): Promise<StateUpdateOutput>;
27
+ applyUpdates(state: StoryStructuredState, updates: StateUpdateOutput, chapterNumber: number): StoryStructuredState;
28
+ }
29
+ export declare const stateUpdater: StateUpdater;
30
+ export {};