@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.
- package/dist/agents/canonValidator.d.ts +9 -0
- package/dist/agents/canonValidator.js +51 -0
- package/dist/agents/chapterPlanner.d.ts +50 -0
- package/dist/agents/chapterPlanner.js +250 -0
- package/dist/agents/completeness.d.ts +7 -0
- package/dist/agents/completeness.js +51 -0
- package/dist/agents/memoryExtractor.d.ts +12 -0
- package/dist/agents/memoryExtractor.js +82 -0
- package/dist/agents/stateUpdater.d.ts +30 -0
- package/dist/agents/stateUpdater.js +150 -0
- package/dist/agents/storyDirector.d.ts +40 -0
- package/dist/agents/storyDirector.js +213 -0
- package/dist/agents/summarizer.d.ts +8 -0
- package/dist/agents/summarizer.js +56 -0
- package/dist/agents/tensionController.d.ts +68 -0
- package/dist/agents/tensionController.js +197 -0
- package/dist/agents/writer.d.ts +12 -0
- package/dist/agents/writer.js +148 -0
- package/dist/constraints/constraintGraph.d.ts +117 -0
- package/dist/constraints/constraintGraph.js +381 -0
- package/dist/constraints/validator.d.ts +58 -0
- package/dist/constraints/validator.js +236 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +115 -0
- package/dist/llm/client.d.ts +14 -0
- package/dist/llm/client.js +108 -0
- package/dist/memory/canonStore.d.ts +20 -0
- package/dist/memory/canonStore.js +110 -0
- package/dist/memory/memoryRetriever.d.ts +28 -0
- package/dist/memory/memoryRetriever.js +126 -0
- package/dist/memory/stateUpdater.d.ts +49 -0
- package/dist/memory/stateUpdater.js +315 -0
- package/dist/memory/vectorStore.d.ts +41 -0
- package/dist/memory/vectorStore.js +166 -0
- package/dist/pipeline/generateChapter.d.ts +17 -0
- package/dist/pipeline/generateChapter.js +75 -0
- package/dist/story/bible.d.ts +4 -0
- package/dist/story/bible.js +53 -0
- package/dist/story/state.d.ts +3 -0
- package/dist/story/state.js +27 -0
- package/dist/story/structuredState.d.ts +39 -0
- package/dist/story/structuredState.js +159 -0
- package/dist/test/canon.test.d.ts +1 -0
- package/dist/test/canon.test.js +104 -0
- package/dist/test/chapter-planner.test.d.ts +1 -0
- package/dist/test/chapter-planner.test.js +171 -0
- package/dist/test/constraints.test.d.ts +1 -0
- package/dist/test/constraints.test.js +210 -0
- package/dist/test/simple.test.d.ts +1 -0
- package/dist/test/simple.test.js +51 -0
- package/dist/test/state-updater.test.d.ts +1 -0
- package/dist/test/state-updater.test.js +200 -0
- package/dist/test/story-director.test.d.ts +1 -0
- package/dist/test/story-director.test.js +142 -0
- package/dist/test/structured-state.test.d.ts +1 -0
- package/dist/test/structured-state.test.js +144 -0
- package/dist/test/tension-controller.test.d.ts +1 -0
- package/dist/test/tension-controller.test.js +116 -0
- package/dist/test/vector-memory.test.d.ts +1 -0
- package/dist/test/vector-memory.test.js +153 -0
- package/dist/test/world-simulation.test.d.ts +1 -0
- package/dist/test/world-simulation.test.js +152 -0
- package/dist/types/index.d.ts +79 -0
- package/dist/types/index.js +3 -0
- package/dist/world/characterAgent.d.ts +73 -0
- package/dist/world/characterAgent.js +232 -0
- package/dist/world/eventResolver.d.ts +52 -0
- package/dist/world/eventResolver.js +205 -0
- package/dist/world/worldState.d.ts +93 -0
- package/dist/world/worldState.js +258 -0
- package/package.json +43 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tensionController = exports.TensionController = void 0;
|
|
4
|
+
exports.calculateTargetTension = calculateTargetTension;
|
|
5
|
+
exports.calculateNextChapterTension = calculateNextChapterTension;
|
|
6
|
+
exports.analyzeTension = analyzeTension;
|
|
7
|
+
exports.generateTensionGuidance = generateTensionGuidance;
|
|
8
|
+
exports.formatTensionForPrompt = formatTensionForPrompt;
|
|
9
|
+
exports.estimateTensionFromChapter = estimateTensionFromChapter;
|
|
10
|
+
/**
|
|
11
|
+
* Calculate target tension using parabolic curve
|
|
12
|
+
* Formula: targetTension = 4 * progress * (1 - progress)
|
|
13
|
+
*
|
|
14
|
+
* This creates a natural dramatic arc:
|
|
15
|
+
* - Chapter 0: 0% tension (setup)
|
|
16
|
+
* - Middle chapters: ~100% tension (peak drama)
|
|
17
|
+
* - Final chapter: 0% tension (resolution)
|
|
18
|
+
*/
|
|
19
|
+
function calculateTargetTension(currentChapter, totalChapters) {
|
|
20
|
+
if (totalChapters <= 1)
|
|
21
|
+
return 0.5;
|
|
22
|
+
// Progress from 0 to 1
|
|
23
|
+
const progress = (currentChapter - 1) / (totalChapters - 1);
|
|
24
|
+
// Parabolic curve: 4 * x * (1 - x)
|
|
25
|
+
// Peak at 0.5 (middle of story)
|
|
26
|
+
const targetTension = 4 * progress * (1 - progress);
|
|
27
|
+
// Round to 2 decimal places
|
|
28
|
+
return Math.round(targetTension * 100) / 100;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Calculate tension for next chapter based on ideal arc
|
|
32
|
+
*/
|
|
33
|
+
function calculateNextChapterTension(currentChapter, totalChapters) {
|
|
34
|
+
return calculateTargetTension(currentChapter + 1, totalChapters);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Analyze current tension vs target and provide guidance
|
|
38
|
+
*/
|
|
39
|
+
function analyzeTension(storyState, structuredState) {
|
|
40
|
+
const targetTension = calculateTargetTension(storyState.currentChapter, storyState.totalChapters);
|
|
41
|
+
const currentTension = structuredState.tension;
|
|
42
|
+
const tensionGap = targetTension - currentTension;
|
|
43
|
+
let recommendedAction;
|
|
44
|
+
let reasoning;
|
|
45
|
+
if (storyState.currentChapter === storyState.totalChapters) {
|
|
46
|
+
recommendedAction = 'resolve';
|
|
47
|
+
reasoning = 'Final chapter - bring tensions to resolution';
|
|
48
|
+
}
|
|
49
|
+
else if (tensionGap > 0.2) {
|
|
50
|
+
recommendedAction = 'escalate';
|
|
51
|
+
reasoning = `Tension is ${(tensionGap * 100).toFixed(0)}% below target. Escalate conflict.`;
|
|
52
|
+
}
|
|
53
|
+
else if (tensionGap < -0.15) {
|
|
54
|
+
recommendedAction = 'maintain';
|
|
55
|
+
reasoning = `Tension is ${(Math.abs(tensionGap) * 100).toFixed(0)}% above target. Allow breathing room.`;
|
|
56
|
+
}
|
|
57
|
+
else if (targetTension > 0.85) {
|
|
58
|
+
recommendedAction = 'climax';
|
|
59
|
+
reasoning = 'Near peak tension - build toward climax';
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
recommendedAction = 'maintain';
|
|
63
|
+
reasoning = 'Tension is on track with target arc';
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
currentTension,
|
|
67
|
+
targetTension,
|
|
68
|
+
tensionGap,
|
|
69
|
+
recommendedAction,
|
|
70
|
+
reasoning,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generate tension guidance for the writer
|
|
75
|
+
*/
|
|
76
|
+
function generateTensionGuidance(analysis, storyState) {
|
|
77
|
+
const { targetTension, recommendedAction } = analysis;
|
|
78
|
+
let guidance;
|
|
79
|
+
let sceneTypes;
|
|
80
|
+
let pacingNotes;
|
|
81
|
+
switch (recommendedAction) {
|
|
82
|
+
case 'escalate':
|
|
83
|
+
guidance = 'Increase dramatic tension. Introduce complications, raise stakes, or deepen conflicts.';
|
|
84
|
+
sceneTypes = ['confrontation', 'discovery', 'setback', 'danger'];
|
|
85
|
+
pacingNotes = 'Faster pace, shorter scenes, more action/dialogue';
|
|
86
|
+
break;
|
|
87
|
+
case 'maintain':
|
|
88
|
+
guidance = 'Maintain current tension level. Balance action with character moments.';
|
|
89
|
+
sceneTypes = ['development', 'interaction', 'preparation', 'reflection'];
|
|
90
|
+
pacingNotes = 'Moderate pace, mix of action and quiet moments';
|
|
91
|
+
break;
|
|
92
|
+
case 'resolve':
|
|
93
|
+
guidance = 'Begin resolving tensions. Answer questions, conclude arcs, provide closure.';
|
|
94
|
+
sceneTypes = ['resolution', 'revelation', 'farewell', 'new beginning'];
|
|
95
|
+
pacingNotes = 'Slower pace, focus on emotional satisfaction';
|
|
96
|
+
break;
|
|
97
|
+
case 'climax':
|
|
98
|
+
guidance = 'Build toward peak tension. Maximum stakes, critical decisions, turning points.';
|
|
99
|
+
sceneTypes = ['climax', 'showdown', 'revelation', 'sacrifice'];
|
|
100
|
+
pacingNotes = 'Fastest pace, continuous escalation, no relief';
|
|
101
|
+
break;
|
|
102
|
+
default:
|
|
103
|
+
guidance = 'Follow the natural flow of the story.';
|
|
104
|
+
sceneTypes = ['mixed'];
|
|
105
|
+
pacingNotes = 'Balanced pacing';
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
targetTension,
|
|
109
|
+
guidance,
|
|
110
|
+
sceneTypes,
|
|
111
|
+
pacingNotes,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Format tension guidance for writer prompt
|
|
116
|
+
*/
|
|
117
|
+
function formatTensionForPrompt(guidance) {
|
|
118
|
+
const tensionPercent = Math.round(guidance.targetTension * 100);
|
|
119
|
+
return `## Tension Guidance
|
|
120
|
+
|
|
121
|
+
**Target Tension Level:** ${tensionPercent}%
|
|
122
|
+
|
|
123
|
+
**Guidance:** ${guidance.guidance}
|
|
124
|
+
|
|
125
|
+
**Recommended Scene Types:** ${guidance.sceneTypes.join(', ')}
|
|
126
|
+
|
|
127
|
+
**Pacing Notes:** ${guidance.pacingNotes}
|
|
128
|
+
`;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Calculate tension based on chapter content analysis
|
|
132
|
+
* This is a heuristic for estimating tension from text
|
|
133
|
+
*/
|
|
134
|
+
function estimateTensionFromChapter(chapter) {
|
|
135
|
+
const content = chapter.content.toLowerCase();
|
|
136
|
+
// Tension indicators
|
|
137
|
+
const tensionWords = [
|
|
138
|
+
'fear', 'danger', 'threat', 'attack', 'fight', 'battle',
|
|
139
|
+
'scream', 'cry', 'panic', 'terror', 'horror',
|
|
140
|
+
'chase', 'escape', 'hide', 'run', 'flee',
|
|
141
|
+
'discover', 'reveal', 'secret', 'mystery', 'truth',
|
|
142
|
+
'confront', 'accuse', 'blame', 'angry', 'furious',
|
|
143
|
+
'worry', 'anxious', 'nervous', 'tense', 'stress',
|
|
144
|
+
'urgent', 'emergency', 'crisis', 'critical', 'desperate',
|
|
145
|
+
];
|
|
146
|
+
const calmWords = [
|
|
147
|
+
'peace', 'calm', 'quiet', 'relax', 'rest',
|
|
148
|
+
'happy', 'joy', 'laugh', 'smile', 'comfort',
|
|
149
|
+
'safe', 'secure', 'peaceful', 'tranquil', 'serene',
|
|
150
|
+
];
|
|
151
|
+
let tensionScore = 0.5; // baseline
|
|
152
|
+
// Count tension indicators
|
|
153
|
+
for (const word of tensionWords) {
|
|
154
|
+
const count = (content.match(new RegExp(word, 'g')) || []).length;
|
|
155
|
+
tensionScore += count * 0.02;
|
|
156
|
+
}
|
|
157
|
+
// Subtract calm indicators
|
|
158
|
+
for (const word of calmWords) {
|
|
159
|
+
const count = (content.match(new RegExp(word, 'g')) || []).length;
|
|
160
|
+
tensionScore -= count * 0.015;
|
|
161
|
+
}
|
|
162
|
+
// Clamp between 0 and 1
|
|
163
|
+
return Math.max(0, Math.min(1, Math.round(tensionScore * 100) / 100));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* TensionController class for managing story tension
|
|
167
|
+
*/
|
|
168
|
+
class TensionController {
|
|
169
|
+
/**
|
|
170
|
+
* Analyze current story state and provide tension guidance
|
|
171
|
+
*/
|
|
172
|
+
analyze(storyState, structuredState) {
|
|
173
|
+
return analyzeTension(storyState, structuredState);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Generate guidance for the next chapter
|
|
177
|
+
*/
|
|
178
|
+
generateGuidance(storyState, structuredState) {
|
|
179
|
+
const analysis = this.analyze(storyState, structuredState);
|
|
180
|
+
return generateTensionGuidance(analysis, storyState);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Calculate what the tension should be for a specific chapter
|
|
184
|
+
*/
|
|
185
|
+
calculateTarget(chapterNumber, totalChapters) {
|
|
186
|
+
return calculateTargetTension(chapterNumber, totalChapters);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Estimate tension from chapter content
|
|
190
|
+
*/
|
|
191
|
+
estimateFromContent(chapter) {
|
|
192
|
+
return estimateTensionFromChapter(chapter);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
exports.TensionController = TensionController;
|
|
196
|
+
exports.tensionController = new TensionController();
|
|
197
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuc2lvbkNvbnRyb2xsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnRzL3RlbnNpb25Db250cm9sbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQTJCQSx3REFlQztBQUtELGtFQUtDO0FBS0Qsd0NBdUNDO0FBS0QsMERBK0NDO0FBS0Qsd0RBYUM7QUFNRCxnRUFvQ0M7QUE5TEQ7Ozs7Ozs7O0dBUUc7QUFDSCxTQUFnQixzQkFBc0IsQ0FDcEMsY0FBc0IsRUFDdEIsYUFBcUI7SUFFckIsSUFBSSxhQUFhLElBQUksQ0FBQztRQUFFLE9BQU8sR0FBRyxDQUFDO0lBRW5DLHVCQUF1QjtJQUN2QixNQUFNLFFBQVEsR0FBRyxDQUFDLGNBQWMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLGFBQWEsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUU1RCxtQ0FBbUM7SUFDbkMsZ0NBQWdDO0lBQ2hDLE1BQU0sYUFBYSxHQUFHLENBQUMsR0FBRyxRQUFRLEdBQUcsQ0FBQyxDQUFDLEdBQUcsUUFBUSxDQUFDLENBQUM7SUFFcEQsNEJBQTRCO0lBQzVCLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxhQUFhLEdBQUcsR0FBRyxDQUFDLEdBQUcsR0FBRyxDQUFDO0FBQy9DLENBQUM7QUFFRDs7R0FFRztBQUNILFNBQWdCLDJCQUEyQixDQUN6QyxjQUFzQixFQUN0QixhQUFxQjtJQUVyQixPQUFPLHNCQUFzQixDQUFDLGNBQWMsR0FBRyxDQUFDLEVBQUUsYUFBYSxDQUFDLENBQUM7QUFDbkUsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsY0FBYyxDQUM1QixVQUFzQixFQUN0QixlQUFxQztJQUVyQyxNQUFNLGFBQWEsR0FBRyxzQkFBc0IsQ0FDMUMsVUFBVSxDQUFDLGNBQWMsRUFDekIsVUFBVSxDQUFDLGFBQWEsQ0FDekIsQ0FBQztJQUVGLE1BQU0sY0FBYyxHQUFHLGVBQWUsQ0FBQyxPQUFPLENBQUM7SUFDL0MsTUFBTSxV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dUJBQXVCLENBQUMsUUFBUSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQ3ZELENBQUM7SUFFRDs7T0FFRztJQUNILGVBQWUsQ0FBQyxhQUFxQixFQUFFLGFBQXFCO1FBQzFELE9BQU8sc0JBQXNCLENBQUMsYUFBYSxFQUFFLGFBQWEsQ0FBQyxDQUFDO0lBQzlELENBQUM7SUFFRDs7T0FFRztJQUNILG1CQUFtQixDQUFDLE9BQWdCO1FBQ2xDLE9BQU8sMEJBQTBCLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDN0MsQ0FBQztDQUNGO0FBbkNELDhDQW1DQztBQUVZLFFBQUEsaUJBQWlCLEdBQUcsSUFBSSxpQkFBaUIsRUFBRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBDaGFwdGVyLCBTdG9yeUJpYmxlLCBTdG9yeVN0YXRlIH0gZnJvbSAnLi4vdHlwZXMvaW5kZXguanMnO1xyXG5pbXBvcnQgdHlwZSB7IFN0b3J5U3RydWN0dXJlZFN0YXRlIH0gZnJvbSAnLi4vc3Rvcnkvc3RydWN0dXJlZFN0YXRlLmpzJztcclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgVGVuc2lvbkFuYWx5c2lzIHtcclxuICBjdXJyZW50VGVuc2lvbjogbnVtYmVyO1xyXG4gIHRhcmdldFRlbnNpb246IG51bWJlcjtcclxuICB0ZW5zaW9uR2FwOiBudW1iZXI7IC8vIHBvc2l0aXZlID0gbmVlZCB0byBpbmNyZWFzZSwgbmVnYXRpdmUgPSBuZWVkIHRvIGRlY3JlYXNlXHJcbiAgcmVjb21tZW5kZWRBY3Rpb246ICdlc2NhbGF0ZScgfCAnbWFpbnRhaW4nIHwgJ3Jlc29sdmUnIHwgJ2NsaW1heCc7XHJcbiAgcmVhc29uaW5nOiBzdHJpbmc7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgVGVuc2lvbkd1aWRhbmNlIHtcclxuICB0YXJnZXRUZW5zaW9uOiBudW1iZXI7XHJcbiAgZ3VpZGFuY2U6IHN0cmluZztcclxuICBzY2VuZVR5cGVzOiBzdHJpbmdbXTtcclxuICBwYWNpbmdOb3Rlczogc3RyaW5nO1xyXG59XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlIHRhcmdldCB0ZW5zaW9uIHVzaW5nIHBhcmFib2xpYyBjdXJ2ZVxyXG4gKiBGb3JtdWxhOiB0YXJnZXRUZW5zaW9uID0gNCAqIHByb2dyZXNzICogKDEgLSBwcm9ncmVzcylcclxuICogXHJcbiAqIFRoaXMgY3JlYXRlcyBhIG5hdHVyYWwgZHJhbWF0aWMgYXJjOlxyXG4gKiAtIENoYXB0ZXIgMDogMCUgdGVuc2lvbiAoc2V0dXApXHJcbiAqIC0gTWlkZGxlIGNoYXB0ZXJzOiB+MTAwJSB0ZW5zaW9uIChwZWFrIGRyYW1hKVxyXG4gKiAtIEZpbmFsIGNoYXB0ZXI6IDAlIHRlbnNpb24gKHJlc29sdXRpb24pXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlVGFyZ2V0VGVuc2lvbihcclxuICBjdXJyZW50Q2hhcHRlcjogbnVtYmVyLFxyXG4gIHRvdGFsQ2hhcHRlcnM6IG51bWJlclxyXG4pOiBudW1iZXIge1xyXG4gIGlmICh0b3RhbENoYXB0ZXJzIDw9IDEpIHJldHVybiAwLjU7XHJcbiAgXHJcbiAgLy8gUHJvZ3Jlc3MgZnJvbSAwIHRvIDFcclxuICBjb25zdCBwcm9ncmVzcyA9IChjdXJyZW50Q2hhcHRlciAtIDEpIC8gKHRvdGFsQ2hhcHRlcnMgLSAxKTtcclxuICBcclxuICAvLyBQYXJhYm9saWMgY3VydmU6IDQgKiB4ICogKDEgLSB4KVxyXG4gIC8vIFBlYWsgYXQgMC41IChtaWRkbGUgb2Ygc3RvcnkpXHJcbiAgY29uc3QgdGFyZ2V0VGVuc2lvbiA9IDQgKiBwcm9ncmVzcyAqICgxIC0gcHJvZ3Jlc3MpO1xyXG4gIFxyXG4gIC8vIFJvdW5kIHRvIDIgZGVjaW1hbCBwbGFjZXNcclxuICByZXR1cm4gTWF0aC5yb3VuZCh0YXJnZXRUZW5zaW9uICogMTAwKSAvIDEwMDtcclxufVxyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZSB0ZW5zaW9uIGZvciBuZXh0IGNoYXB0ZXIgYmFzZWQgb24gaWRlYWwgYXJjXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlTmV4dENoYXB0ZXJUZW5zaW9uKFxyXG4gIGN1cnJlbnRDaGFwdGVyOiBudW1iZXIsXHJcbiAgdG90YWxDaGFwdGVyczogbnVtYmVyXHJcbik6IG51bWJlciB7XHJcbiAgcmV0dXJuIGNhbGN1bGF0ZVRhcmdldFRlbnNpb24oY3VycmVudENoYXB0ZXIgKyAxLCB0b3RhbENoYXB0ZXJzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFuYWx5emUgY3VycmVudCB0ZW5zaW9uIHZzIHRhcmdldCBhbmQgcHJvdmlkZSBndWlkYW5jZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGFuYWx5emVUZW5zaW9uKFxyXG4gIHN0b3J5U3RhdGU6IFN0b3J5U3RhdGUsXHJcbiAgc3RydWN0dXJlZFN0YXRlOiBTdG9yeVN0cnVjdHVyZWRTdGF0ZVxyXG4pOiBUZW5zaW9uQW5hbHlzaXMge1xyXG4gIGNvbnN0IHRhcmdldFRlbnNpb24gPSBjYWxjdWxhdGVUYXJnZXRUZW5zaW9uKFxyXG4gICAgc3RvcnlTdGF0ZS5jdXJyZW50Q2hhcHRlcixcclxuICAgIHN0b3J5U3RhdGUudG90YWxDaGFwdGVyc1xyXG4gICk7XHJcbiAgXHJcbiAgY29uc3QgY3VycmVudFRlbnNpb24gPSBzdHJ1Y3R1cmVkU3RhdGUudGVuc2lvbjtcclxuICBjb25zdCB0ZW5zaW9uR2FwID0gdGFyZ2V0VGVuc2lvbiAtIGN1cnJlbnRUZW5zaW9uO1xyXG4gIFxyXG4gIGxldCByZWNvbW1lbmRlZEFjdGlvbjogVGVuc2lvbkFuYWx5c2lzWydyZWNvbW1lbmRlZEFjdGlvbiddO1xyXG4gIGxldCByZWFzb25pbmc6IHN0cmluZztcclxuICBcclxuICBpZiAoc3RvcnlTdGF0ZS5jdXJyZW50Q2hhcHRlciA9PT0gc3RvcnlTdGF0ZS50b3RhbENoYXB0ZXJzKSB7XHJcbiAgICByZWNvbW1lbmRlZEFjdGlvbiA9ICdyZXNvbHZlJztcclxuICAgIHJlYXNvbmluZyA9ICdGaW5hbCBjaGFwdGVyIC0gYnJpbmcgdGVuc2lvbnMgdG8gcmVzb2x1dGlvbic7XHJcbiAgfSBlbHNlIGlmICh0ZW5zaW9uR2FwID4gMC4yKSB7XHJcbiAgICByZWNvbW1lbmRlZEFjdGlvbiA9ICdlc2NhbGF0ZSc7XHJcbiAgICByZWFzb25pbmcgPSBgVGVuc2lvbiBpcyAkeyh0ZW5zaW9uR2FwICogMTAwKS50b0ZpeGVkKDApfSUgYmVsb3cgdGFyZ2V0LiBFc2NhbGF0ZSBjb25mbGljdC5gO1xyXG4gIH0gZWxzZSBpZiAodGVuc2lvbkdhcCA8IC0wLjE1KSB7XHJcbiAgICByZWNvbW1lbmRlZEFjdGlvbiA9ICdtYWludGFpbic7XHJcbiAgICByZWFzb25pbmcgPSBgVGVuc2lvbiBpcyAkeyhNYXRoLmFicyh0ZW5zaW9uR2FwKSAqIDEwMCkudG9GaXhlZCgwKX0lIGFib3ZlIHRhcmdldC4gQWxsb3cgYnJlYXRoaW5nIHJvb20uYDtcclxuICB9IGVsc2UgaWYgKHRhcmdldFRlbnNpb24gPiAwLjg1KSB7XHJcbiAgICByZWNvbW1lbmRlZEFjdGlvbiA9ICdjbGltYXgnO1xyXG4gICAgcmVhc29uaW5nID0gJ05lYXIgcGVhayB0ZW5zaW9uIC0gYnVpbGQgdG93YXJkIGNsaW1heCc7XHJcbiAgfSBlbHNlIHtcclxuICAgIHJlY29tbWVuZGVkQWN0aW9uID0gJ21haW50YWluJztcclxuICAgIHJlYXNvbmluZyA9ICdUZW5zaW9uIGlzIG9uIHRyYWNrIHdpdGggdGFyZ2V0IGFyYyc7XHJcbiAgfVxyXG4gIFxyXG4gIHJldHVybiB7XHJcbiAgICBjdXJyZW50VGVuc2lvbixcclxuICAgIHRhcmdldFRlbnNpb24sXHJcbiAgICB0ZW5zaW9uR2FwLFxyXG4gICAgcmVjb21tZW5kZWRBY3Rpb24sXHJcbiAgICByZWFzb25pbmcsXHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdlbmVyYXRlIHRlbnNpb24gZ3VpZGFuY2UgZm9yIHRoZSB3cml0ZXJcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZVRlbnNpb25HdWlkYW5jZShcclxuICBhbmFseXNpczogVGVuc2lvbkFuYWx5c2lzLFxyXG4gIHN0b3J5U3RhdGU6IFN0b3J5U3RhdGVcclxuKTogVGVuc2lvbkd1aWRhbmNlIHtcclxuICBjb25zdCB7IHRhcmdldFRlbnNpb24sIHJlY29tbWVuZGVkQWN0aW9uIH0gPSBhbmFseXNpcztcclxuICBcclxuICBsZXQgZ3VpZGFuY2U6IHN0cmluZztcclxuICBsZXQgc2NlbmVUeXBlczogc3RyaW5nW107XHJcbiAgbGV0IHBhY2luZ05vdGVzOiBzdHJpbmc7XHJcbiAgXHJcbiAgc3dpdGNoIChyZWNvbW1lbmRlZEFjdGlvbikge1xyXG4gICAgY2FzZSAnZXNjYWxhdGUnOlxyXG4gICAgICBndWlkYW5jZSA9ICdJbmNyZWFzZSBkcmFtYXRpYyB0ZW5zaW9uLiBJbnRyb2R1Y2UgY29tcGxpY2F0aW9ucywgcmFpc2Ugc3Rha2VzLCBvciBkZWVwZW4gY29uZmxpY3RzLic7XHJcbiAgICAgIHNjZW5lVHlwZXMgPSBbJ2NvbmZyb250YXRpb24nLCAnZGlzY292ZXJ5JywgJ3NldGJhY2snLCAnZGFuZ2VyJ107XHJcbiAgICAgIHBhY2luZ05vdGVzID0gJ0Zhc3RlciBwYWNlLCBzaG9ydGVyIHNjZW5lcywgbW9yZSBhY3Rpb24vZGlhbG9ndWUnO1xyXG4gICAgICBicmVhaztcclxuICAgICAgXHJcbiAgICBjYXNlICdtYWludGFpbic6XHJcbiAgICAgIGd1aWRhbmNlID0gJ01haW50YWluIGN1cnJlbnQgdGVuc2lvbiBsZXZlbC4gQmFsYW5jZSBhY3Rpb24gd2l0aCBjaGFyYWN0ZXIgbW9tZW50cy4nO1xyXG4gICAgICBzY2VuZVR5cGVzID0gWydkZXZlbG9wbWVudCcsICdpbnRlcmFjdGlvbicsICdwcmVwYXJhdGlvbicsICdyZWZsZWN0aW9uJ107XHJcbiAgICAgIHBhY2luZ05vdGVzID0gJ01vZGVyYXRlIHBhY2UsIG1peCBvZiBhY3Rpb24gYW5kIHF1aWV0IG1vbWVudHMnO1xyXG4gICAgICBicmVhaztcclxuICAgICAgXHJcbiAgICBjYXNlICdyZXNvbHZlJzpcclxuICAgICAgZ3VpZGFuY2UgPSAnQmVnaW4gcmVzb2x2aW5nIHRlbnNpb25zLiBBbnN3ZXIgcXVlc3Rpb25zLCBjb25jbHVkZSBhcmNzLCBwcm92aWRlIGNsb3N1cmUuJztcclxuICAgICAgc2NlbmVUeXBlcyA9IFsncmVzb2x1dGlvbicsICdyZXZlbGF0aW9uJywgJ2ZhcmV3ZWxsJywgJ25ldyBiZWdpbm5pbmcnXTtcclxuICAgICAgcGFjaW5nTm90ZXMgPSAnU2xvd2VyIHBhY2UsIGZvY3VzIG9uIGVtb3Rpb25hbCBzYXRpc2ZhY3Rpb24nO1xyXG4gICAgICBicmVhaztcclxuICAgICAgXHJcbiAgICBjYXNlICdjbGltYXgnOlxyXG4gICAgICBndWlkYW5jZSA9ICdCdWlsZCB0b3dhcmQgcGVhayB0ZW5zaW9uLiBNYXhpbXVtIHN0YWtlcywgY3JpdGljYWwgZGVjaXNpb25zLCB0dXJuaW5nIHBvaW50cy4nO1xyXG4gICAgICBzY2VuZVR5cGVzID0gWydjbGltYXgnLCAnc2hvd2Rvd24nLCAncmV2ZWxhdGlvbicsICdzYWNyaWZpY2UnXTtcclxuICAgICAgcGFjaW5nTm90ZXMgPSAnRmFzdGVzdCBwYWNlLCBjb250aW51b3VzIGVzY2FsYXRpb24sIG5vIHJlbGllZic7XHJcbiAgICAgIGJyZWFrO1xyXG4gICAgICBcclxuICAgIGRlZmF1bHQ6XHJcbiAgICAgIGd1aWRhbmNlID0gJ0ZvbGxvdyB0aGUgbmF0dXJhbCBmbG93IG9mIHRoZSBzdG9yeS4nO1xyXG4gICAgICBzY2VuZVR5cGVzID0gWydtaXhlZCddO1xyXG4gICAgICBwYWNpbmdOb3RlcyA9ICdCYWxhbmNlZCBwYWNpbmcnO1xyXG4gIH1cclxuICBcclxuICByZXR1cm4ge1xyXG4gICAgdGFyZ2V0VGVuc2lvbixcclxuICAgIGd1aWRhbmNlLFxyXG4gICAgc2NlbmVUeXBlcyxcclxuICAgIHBhY2luZ05vdGVzLFxyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGb3JtYXQgdGVuc2lvbiBndWlkYW5jZSBmb3Igd3JpdGVyIHByb21wdFxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFRlbnNpb25Gb3JQcm9tcHQoZ3VpZGFuY2U6IFRlbnNpb25HdWlkYW5jZSk6IHN0cmluZyB7XHJcbiAgY29uc3QgdGVuc2lvblBlcmNlbnQgPSBNYXRoLnJvdW5kKGd1aWRhbmNlLnRhcmdldFRlbnNpb24gKiAxMDApO1xyXG4gIFxyXG4gIHJldHVybiBgIyMgVGVuc2lvbiBHdWlkYW5jZVxyXG5cclxuKipUYXJnZXQgVGVuc2lvbiBMZXZlbDoqKiAke3RlbnNpb25QZXJjZW50fSVcclxuXHJcbioqR3VpZGFuY2U6KiogJHtndWlkYW5jZS5ndWlkYW5jZX1cclxuXHJcbioqUmVjb21tZW5kZWQgU2NlbmUgVHlwZXM6KiogJHtndWlkYW5jZS5zY2VuZVR5cGVzLmpvaW4oJywgJyl9XHJcblxyXG4qKlBhY2luZyBOb3RlczoqKiAke2d1aWRhbmNlLnBhY2luZ05vdGVzfVxyXG5gO1xyXG59XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlIHRlbnNpb24gYmFzZWQgb24gY2hhcHRlciBjb250ZW50IGFuYWx5c2lzXHJcbiAqIFRoaXMgaXMgYSBoZXVyaXN0aWMgZm9yIGVzdGltYXRpbmcgdGVuc2lvbiBmcm9tIHRleHRcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlc3RpbWF0ZVRlbnNpb25Gcm9tQ2hhcHRlcihjaGFwdGVyOiBDaGFwdGVyKTogbnVtYmVyIHtcclxuICBjb25zdCBjb250ZW50ID0gY2hhcHRlci5jb250ZW50LnRvTG93ZXJDYXNlKCk7XHJcbiAgXHJcbiAgLy8gVGVuc2lvbiBpbmRpY2F0b3JzXHJcbiAgY29uc3QgdGVuc2lvbldvcmRzID0gW1xyXG4gICAgJ2ZlYXInLCAnZGFuZ2VyJywgJ3RocmVhdCcsICdhdHRhY2snLCAnZmlnaHQnLCAnYmF0dGxlJyxcclxuICAgICdzY3JlYW0nLCAnY3J5JywgJ3BhbmljJywgJ3RlcnJvcicsICdob3Jyb3InLFxyXG4gICAgJ2NoYXNlJywgJ2VzY2FwZScsICdoaWRlJywgJ3J1bicsICdmbGVlJyxcclxuICAgICdkaXNjb3ZlcicsICdyZXZlYWwnLCAnc2VjcmV0JywgJ215c3RlcnknLCAndHJ1dGgnLFxyXG4gICAgJ2NvbmZyb250JywgJ2FjY3VzZScsICdibGFtZScsICdhbmdyeScsICdmdXJpb3VzJyxcclxuICAgICd3b3JyeScsICdhbnhpb3VzJywgJ25lcnZvdXMnLCAndGVuc2UnLCAnc3RyZXNzJyxcclxuICAgICd1cmdlbnQnLCAnZW1lcmdlbmN5JywgJ2NyaXNpcycsICdjcml0aWNhbCcsICdkZXNwZXJhdGUnLFxyXG4gIF07XHJcbiAgXHJcbiAgY29uc3QgY2FsbVdvcmRzID0gW1xyXG4gICAgJ3BlYWNlJywgJ2NhbG0nLCAncXVpZXQnLCAncmVsYXgnLCAncmVzdCcsXHJcbiAgICAnaGFwcHknLCAnam95JywgJ2xhdWdoJywgJ3NtaWxlJywgJ2NvbWZvcnQnLFxyXG4gICAgJ3NhZmUnLCAnc2VjdXJlJywgJ3BlYWNlZnVsJywgJ3RyYW5xdWlsJywgJ3NlcmVuZScsXHJcbiAgXTtcclxuICBcclxuICBsZXQgdGVuc2lvblNjb3JlID0gMC41OyAvLyBiYXNlbGluZVxyXG4gIFxyXG4gIC8vIENvdW50IHRlbnNpb24gaW5kaWNhdG9yc1xyXG4gIGZvciAoY29uc3Qgd29yZCBvZiB0ZW5zaW9uV29yZHMpIHtcclxuICAgIGNvbnN0IGNvdW50ID0gKGNvbnRlbnQubWF0Y2gobmV3IFJlZ0V4cCh3b3JkLCAnZycpKSB8fCBbXSkubGVuZ3RoO1xyXG4gICAgdGVuc2lvblNjb3JlICs9IGNvdW50ICogMC4wMjtcclxuICB9XHJcbiAgXHJcbiAgLy8gU3VidHJhY3QgY2FsbSBpbmRpY2F0b3JzXHJcbiAgZm9yIChjb25zdCB3b3JkIG9mIGNhbG1Xb3Jkcykge1xyXG4gICAgY29uc3QgY291bnQgPSAoY29udGVudC5tYXRjaChuZXcgUmVnRXhwKHdvcmQsICdnJykpIHx8IFtdKS5sZW5ndGg7XHJcbiAgICB0ZW5zaW9uU2NvcmUgLT0gY291bnQgKiAwLjAxNTtcclxuICB9XHJcbiAgXHJcbiAgLy8gQ2xhbXAgYmV0d2VlbiAwIGFuZCAxXHJcbiAgcmV0dXJuIE1hdGgubWF4KDAsIE1hdGgubWluKDEsIE1hdGgucm91bmQodGVuc2lvblNjb3JlICogMTAwKSAvIDEwMCkpO1xyXG59XHJcblxyXG4vKipcclxuICogVGVuc2lvbkNvbnRyb2xsZXIgY2xhc3MgZm9yIG1hbmFnaW5nIHN0b3J5IHRlbnNpb25cclxuICovXHJcbmV4cG9ydCBjbGFzcyBUZW5zaW9uQ29udHJvbGxlciB7XHJcbiAgLyoqXHJcbiAgICogQW5hbHl6ZSBjdXJyZW50IHN0b3J5IHN0YXRlIGFuZCBwcm92aWRlIHRlbnNpb24gZ3VpZGFuY2VcclxuICAgKi9cclxuICBhbmFseXplKFxyXG4gICAgc3RvcnlTdGF0ZTogU3RvcnlTdGF0ZSxcclxuICAgIHN0cnVjdHVyZWRTdGF0ZTogU3RvcnlTdHJ1Y3R1cmVkU3RhdGVcclxuICApOiBUZW5zaW9uQW5hbHlzaXMge1xyXG4gICAgcmV0dXJuIGFuYWx5emVUZW5zaW9uKHN0b3J5U3RhdGUsIHN0cnVjdHVyZWRTdGF0ZSk7XHJcbiAgfVxyXG4gIFxyXG4gIC8qKlxyXG4gICAqIEdlbmVyYXRlIGd1aWRhbmNlIGZvciB0aGUgbmV4dCBjaGFwdGVyXHJcbiAgICovXHJcbiAgZ2VuZXJhdGVHdWlkYW5jZShcclxuICAgIHN0b3J5U3RhdGU6IFN0b3J5U3RhdGUsXHJcbiAgICBzdHJ1Y3R1cmVkU3RhdGU6IFN0b3J5U3RydWN0dXJlZFN0YXRlXHJcbiAgKTogVGVuc2lvbkd1aWRhbmNlIHtcclxuICAgIGNvbnN0IGFuYWx5c2lzID0gdGhpcy5hbmFseXplKHN0b3J5U3RhdGUsIHN0cnVjdHVyZWRTdGF0ZSk7XHJcbiAgICByZXR1cm4gZ2VuZXJhdGVUZW5zaW9uR3VpZGFuY2UoYW5hbHlzaXMsIHN0b3J5U3RhdGUpO1xyXG4gIH1cclxuICBcclxuICAvKipcclxuICAgKiBDYWxjdWxhdGUgd2hhdCB0aGUgdGVuc2lvbiBzaG91bGQgYmUgZm9yIGEgc3BlY2lmaWMgY2hhcHRlclxyXG4gICAqL1xyXG4gIGNhbGN1bGF0ZVRhcmdldChjaGFwdGVyTnVtYmVyOiBudW1iZXIsIHRvdGFsQ2hhcHRlcnM6IG51bWJlcik6IG51bWJlciB7XHJcbiAgICByZXR1cm4gY2FsY3VsYXRlVGFyZ2V0VGVuc2lvbihjaGFwdGVyTnVtYmVyLCB0b3RhbENoYXB0ZXJzKTtcclxuICB9XHJcbiAgXHJcbiAgLyoqXHJcbiAgICogRXN0aW1hdGUgdGVuc2lvbiBmcm9tIGNoYXB0ZXIgY29udGVudFxyXG4gICAqL1xyXG4gIGVzdGltYXRlRnJvbUNvbnRlbnQoY2hhcHRlcjogQ2hhcHRlcik6IG51bWJlciB7XHJcbiAgICByZXR1cm4gZXN0aW1hdGVUZW5zaW9uRnJvbUNoYXB0ZXIoY2hhcHRlcik7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgY29uc3QgdGVuc2lvbkNvbnRyb2xsZXIgPSBuZXcgVGVuc2lvbkNvbnRyb2xsZXIoKTtcclxuIl19
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { GenerationContext, WriterOutput } from '../types/index.js';
|
|
2
|
+
import type { CanonStore } from '../memory/canonStore.js';
|
|
3
|
+
import type { MemoryRetriever } from '../memory/memoryRetriever.js';
|
|
4
|
+
export declare class ChapterWriter {
|
|
5
|
+
private promptTemplate;
|
|
6
|
+
constructor();
|
|
7
|
+
write(context: GenerationContext, canon?: CanonStore, memoryRetriever?: MemoryRetriever): Promise<WriterOutput>;
|
|
8
|
+
continue(existingContent: string, context: GenerationContext): Promise<string>;
|
|
9
|
+
private inferChapterGoal;
|
|
10
|
+
private extractTitle;
|
|
11
|
+
}
|
|
12
|
+
export declare const writer: ChapterWriter;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.writer = exports.ChapterWriter = void 0;
|
|
4
|
+
const client_js_1 = require("../llm/client.js");
|
|
5
|
+
const canonStore_js_1 = require("../memory/canonStore.js");
|
|
6
|
+
const WRITER_PROMPT = `You are a professional novelist writing immersive narrative prose.
|
|
7
|
+
|
|
8
|
+
Write Chapter {{chapterNumber}} of the novel.
|
|
9
|
+
|
|
10
|
+
## Story Bible
|
|
11
|
+
|
|
12
|
+
**Title:** {{title}}
|
|
13
|
+
**Theme:** {{theme}}
|
|
14
|
+
**Genre:** {{genre}}
|
|
15
|
+
**Setting:** {{setting}}
|
|
16
|
+
**Tone:** {{tone}}
|
|
17
|
+
|
|
18
|
+
**Premise:**
|
|
19
|
+
{{premise}}
|
|
20
|
+
|
|
21
|
+
## Characters
|
|
22
|
+
|
|
23
|
+
{{characters}}
|
|
24
|
+
|
|
25
|
+
## Story Canon
|
|
26
|
+
|
|
27
|
+
{{canon}}
|
|
28
|
+
|
|
29
|
+
## Relevant Memories
|
|
30
|
+
|
|
31
|
+
{{memories}}
|
|
32
|
+
|
|
33
|
+
## Recent Chapter Summaries
|
|
34
|
+
|
|
35
|
+
{{recentSummaries}}
|
|
36
|
+
|
|
37
|
+
## Chapter Goal
|
|
38
|
+
|
|
39
|
+
{{chapterGoal}}
|
|
40
|
+
|
|
41
|
+
## Writing Guidelines
|
|
42
|
+
|
|
43
|
+
- Write in third person limited perspective
|
|
44
|
+
- Show, don't tell
|
|
45
|
+
- Maintain consistent character voices
|
|
46
|
+
- Build toward the chapter goal naturally
|
|
47
|
+
- End at a natural stopping point (not mid-scene)
|
|
48
|
+
- Reference relevant past memories naturally when appropriate
|
|
49
|
+
- Target length: {{targetWordCount}} words
|
|
50
|
+
|
|
51
|
+
Write the full chapter now.`;
|
|
52
|
+
class ChapterWriter {
|
|
53
|
+
promptTemplate;
|
|
54
|
+
constructor() {
|
|
55
|
+
this.promptTemplate = WRITER_PROMPT;
|
|
56
|
+
}
|
|
57
|
+
async write(context, canon, memoryRetriever) {
|
|
58
|
+
const { bible, state, chapterNumber, targetWordCount = 2000 } = context;
|
|
59
|
+
const canonSection = canon ? (0, canonStore_js_1.formatCanonForPrompt)(canon) : '';
|
|
60
|
+
// Retrieve relevant memories
|
|
61
|
+
let memoriesSection = 'No relevant memories yet.';
|
|
62
|
+
if (memoryRetriever && chapterNumber > 1) {
|
|
63
|
+
const memories = await memoryRetriever.retrieveForChapter({
|
|
64
|
+
bible,
|
|
65
|
+
state,
|
|
66
|
+
currentChapter: chapterNumber,
|
|
67
|
+
});
|
|
68
|
+
memoriesSection = memoryRetriever.formatMemoriesForPrompt(memories) || 'No highly relevant memories for this chapter.';
|
|
69
|
+
}
|
|
70
|
+
const recentSummaries = state.chapterSummaries
|
|
71
|
+
.slice(-3)
|
|
72
|
+
.map(s => `Chapter ${s.chapterNumber}: ${s.summary}`)
|
|
73
|
+
.join('\n\n') || 'This is the first chapter.';
|
|
74
|
+
const characters = bible.characters
|
|
75
|
+
.map(c => `- ${c.name} (${c.role}): ${c.personality.join(', ')}. Goals: ${c.goals.join(', ')}`)
|
|
76
|
+
.join('\n');
|
|
77
|
+
const chapterGoal = this.inferChapterGoal(bible, state, chapterNumber);
|
|
78
|
+
const prompt = this.promptTemplate
|
|
79
|
+
.replace('{{chapterNumber}}', chapterNumber.toString())
|
|
80
|
+
.replace('{{title}}', bible.title)
|
|
81
|
+
.replace('{{theme}}', bible.theme)
|
|
82
|
+
.replace('{{genre}}', bible.genre)
|
|
83
|
+
.replace('{{setting}}', bible.setting)
|
|
84
|
+
.replace('{{tone}}', bible.tone)
|
|
85
|
+
.replace('{{premise}}', bible.premise)
|
|
86
|
+
.replace('{{characters}}', characters)
|
|
87
|
+
.replace('{{memories}}', memoriesSection)
|
|
88
|
+
.replace('{{recentSummaries}}', recentSummaries)
|
|
89
|
+
.replace('{{chapterGoal}}', chapterGoal)
|
|
90
|
+
.replace('{{targetWordCount}}', targetWordCount.toString())
|
|
91
|
+
.replace('{{canon}}', canonSection);
|
|
92
|
+
const content = await (0, client_js_1.getLLM)().complete(prompt, {
|
|
93
|
+
temperature: 0.8,
|
|
94
|
+
maxTokens: 4000,
|
|
95
|
+
});
|
|
96
|
+
const title = this.extractTitle(content) || `Chapter ${chapterNumber}`;
|
|
97
|
+
const wordCount = content.split(/\s+/).length;
|
|
98
|
+
return { content, title, wordCount };
|
|
99
|
+
}
|
|
100
|
+
async continue(existingContent, context) {
|
|
101
|
+
const prompt = `Continue the following chapter from where it left off. Do not repeat what has already been written. Write the continuation only.
|
|
102
|
+
|
|
103
|
+
## Story Context
|
|
104
|
+
|
|
105
|
+
${context.bible.premise}
|
|
106
|
+
|
|
107
|
+
## Existing Chapter Content
|
|
108
|
+
|
|
109
|
+
${existingContent}
|
|
110
|
+
|
|
111
|
+
## Continuation
|
|
112
|
+
|
|
113
|
+
Continue naturally from the last sentence:`;
|
|
114
|
+
const continuation = await (0, client_js_1.getLLM)().complete(prompt, {
|
|
115
|
+
temperature: 0.8,
|
|
116
|
+
maxTokens: 2000,
|
|
117
|
+
});
|
|
118
|
+
return existingContent + '\n\n' + continuation;
|
|
119
|
+
}
|
|
120
|
+
inferChapterGoal(bible, state, chapterNumber) {
|
|
121
|
+
const progress = chapterNumber / bible.targetChapters;
|
|
122
|
+
if (progress < 0.2) {
|
|
123
|
+
return 'Establish the setting, introduce the main character, and hint at the central conflict.';
|
|
124
|
+
}
|
|
125
|
+
else if (progress < 0.5) {
|
|
126
|
+
return 'Develop the conflict, introduce complications, and raise stakes.';
|
|
127
|
+
}
|
|
128
|
+
else if (progress < 0.8) {
|
|
129
|
+
return 'Escalate tension, reveal important information, and move toward climax.';
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
return 'Build toward resolution and begin tying up plot threads.';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
extractTitle(content) {
|
|
136
|
+
const lines = content.split('\n');
|
|
137
|
+
for (const line of lines.slice(0, 10)) {
|
|
138
|
+
const trimmed = line.trim();
|
|
139
|
+
if (trimmed.startsWith('# ') || trimmed.startsWith('Chapter')) {
|
|
140
|
+
return trimmed.replace(/^#\s*/, '').trim();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.ChapterWriter = ChapterWriter;
|
|
147
|
+
exports.writer = new ChapterWriter();
|
|
148
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3JpdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FnZW50cy93cml0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsZ0RBQTBDO0FBRzFDLDJEQUErRDtBQUcvRCxNQUFNLGFBQWEsR0FBRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzRCQTZDTSxDQUFDO0FBRTdCLE1BQWEsYUFBYTtJQUNoQixjQUFjLENBQVM7SUFFL0I7UUFDRSxJQUFJLENBQUMsY0FBYyxHQUFHLGFBQWEsQ0FBQztJQUN0QyxDQUFDO0lBRUQsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUEwQixFQUFFLEtBQWtCLEVBQUUsZUFBaUM7UUFDM0YsTUFBTSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsYUFBYSxFQUFFLGVBQWUsR0FBRyxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUM7UUFFeEUsTUFBTSxZQUFZLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFBLG9DQUFvQixFQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFFOUQsNkJBQTZCO1FBQzdCLElBQUksZUFBZSxHQUFHLDJCQUEyQixDQUFDO1FBQ2xELElBQUksZUFBZSxJQUFJLGFBQWEsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN6QyxNQUFNLFFBQVEsR0FBRyxNQUFNLGVBQWUsQ0FBQyxrQkFBa0IsQ0FBQztnQkFDeEQsS0FBSztnQkFDTCxLQUFLO2dCQUNMLGNBQWMsRUFBRSxhQUFhO2FBQzlCLENBQUMsQ0FBQztZQUNILGVBQWUsR0FBRyxlQUFlLENBQUMsdUJBQXVCLENBQUMsUUFBUSxDQUFDLElBQUksK0NBQStDLENBQUM7UUFDekgsQ0FBQztRQUVELE1BQU0sZUFBZSxHQUFHLEtBQUssQ0FBQyxnQkFBZ0I7YUFDM0MsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQ1QsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFF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LHdGQUF3RixDQUFDO1FBQ2xHLENBQUM7YUFBTSxJQUFJLFFBQVEsR0FBRyxHQUFHLEVBQUUsQ0FBQztZQUMxQixPQUFPLGtFQUFrRSxDQUFDO1FBQzVFLENBQUM7YUFBTSxJQUFJLFFBQVEsR0FBRyxHQUFHLEVBQUUsQ0FBQztZQUMxQixPQUFPLHlFQUF5RSxDQUFDO1FBQ25GLENBQUM7YUFBTSxDQUFDO1lBQ04sT0FBTywwREFBMEQsQ0FBQztRQUNwRSxDQUFDO0lBQ0gsQ0FBQztJQUVPLFlBQVksQ0FBQyxPQUFlO1FBQ2xDLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbEMsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ3RDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUM1QixJQUFJLE9BQU8sQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksT0FBTyxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDO2dCQUM5RCxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1lBQzdDLENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0NBQ0Y7QUEzR0Qsc0NBMkdDO0FBRVksUUFBQSxNQUFNLEdBQUcsSUFBSSxhQUFhLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGdldExMTSB9IGZyb20gJy4uL2xsbS9jbGllbnQuanMnO1xyXG5pbXBvcnQgdHlwZSB7IEdlbmVyYXRpb25Db250ZXh0LCBXcml0ZXJPdXRwdXQgfSBmcm9tICcuLi90eXBlcy9pbmRleC5qcyc7XHJcbmltcG9ydCB0eXBlIHsgQ2Fub25TdG9yZSB9IGZyb20gJy4uL21lbW9yeS9jYW5vblN0b3JlLmpzJztcclxuaW1wb3J0IHsgZm9ybWF0Q2Fub25Gb3JQcm9tcHQgfSBmcm9tICcuLi9tZW1vcnkvY2Fub25TdG9yZS5qcyc7XHJcbmltcG9ydCB0eXBlIHsgTWVtb3J5UmV0cmlldmVyIH0gZnJvbSAnLi4vbWVtb3J5L21lbW9yeVJldHJpZXZlci5qcyc7XHJcblxyXG5jb25zdCBXUklURVJfUFJPTVBUID0gYFlvdSBhcmUgYSBwcm9mZXNzaW9uYWwgbm92ZWxpc3Qgd3JpdGluZyBpbW1lcnNpdmUgbmFycmF0aXZlIHByb3NlLlxyXG5cclxuV3JpdGUgQ2hhcHRlciB7e2NoYXB0ZXJOdW1iZXJ9fSBvZiB0aGUgbm92ZWwuXHJcblxyXG4jIyBTdG9yeSBCaWJsZVxyXG5cclxuKipUaXRsZToqKiB7e3RpdGxlfX1cclxuKipUaGVtZToqKiB7e3RoZW1lfX1cclxuKipHZW5yZToqKiB7e2dlbnJlfX1cclxuKipTZXR0aW5nOioqIHt7c2V0dGluZ319XHJcbioqVG9uZToqKiB7e3RvbmV9fVxyXG5cclxuKipQcmVtaXNlOioqXHJcbnt7cHJlbWlzZX19XHJcblxyXG4jIyBDaGFyYWN0ZXJzXHJcblxyXG57e2NoYXJhY3RlcnN9fVxyXG5cclxuIyMgU3RvcnkgQ2Fub25cclxuXHJcbnt7Y2Fub259fVxyXG5cclxuIyMgUmVsZXZhbnQgTWVtb3JpZXNcclxuXHJcbnt7bWVtb3JpZXN9fVxyXG5cclxuIyMgUmVjZW50IENoYXB0ZXIgU3VtbWFyaWVzXHJcblxyXG57e3JlY2VudFN1bW1hcmllc319XHJcblxyXG4jIyBDaGFwdGVyIEdvYWxcclxuXHJcbnt7Y2hhcHRlckdvYWx9fVxyXG5cclxuIyMgV3JpdGluZyBHdWlkZWxpbmVzXHJcblxyXG4tIFdyaXRlIGluIHRoaXJkIHBlcnNvbiBsaW1pdGVkIHBlcnNwZWN0aXZlXHJcbi0gU2hvdywgZG9uJ3QgdGVsbFxyXG4tIE1haW50YWluIGNvbnNpc3RlbnQgY2hhcmFjdGVyIHZvaWNlc1xyXG4tIEJ1aWxkIHRvd2FyZCB0aGUgY2hhcHRlciBnb2FsIG5hdHVyYWxseVxyXG4tIEVuZCBhdCBhIG5hdHVyYWwgc3RvcHBpbmcgcG9pbnQgKG5vdCBtaWQtc2NlbmUpXHJcbi0gUmVmZXJlbmNlIHJlbGV2YW50IHBhc3QgbWVtb3JpZXMgbmF0dXJhbGx5IHdoZW4gYXBwcm9wcmlhdGVcclxuLSBUYXJnZXQgbGVuZ3RoOiB7e3RhcmdldFdvcmRDb3VudH19IHdvcmRzXHJcblxyXG5Xcml0ZSB0aGUgZnVsbCBjaGFwdGVyIG5vdy5gO1xyXG5cclxuZXhwb3J0IGNsYXNzIENoYXB0ZXJXcml0ZXIge1xyXG4gIHByaXZhdGUgcHJvbXB0VGVtcGxhdGU6IHN0cmluZztcclxuXHJcbiAgY29uc3RydWN0b3IoKSB7XHJcbiAgICB0aGlzLnByb21wdFRlbXBsYXRlID0gV1JJVEVSX1BST01QVDtcclxuICB9XHJcblxyXG4gIGFzeW5jIHdyaXRlKGNvbnRleHQ6IEdlbmVyYXRpb25Db250ZXh0LCBjYW5vbj86IENhbm9uU3RvcmUsIG1lbW9yeVJldHJpZXZlcj86IE1lbW9yeVJldHJpZXZlcik6IFByb21pc2U8V3JpdGVyT3V0cHV0PiB7XHJcbiAgICBjb25zdCB7IGJpYmxlLCBzdGF0ZSwgY2hhcHRlck51bWJlciwgdGFyZ2V0V29yZENvdW50ID0gMjAwMCB9ID0gY29udGV4dDtcclxuICAgIFxyXG4gICAgY29uc3QgY2Fub25TZWN0aW9uID0gY2Fub24gPyBmb3JtYXRDYW5vbkZvclByb21wdChjYW5vbikgOiAnJztcclxuXHJcbiAgICAvLyBSZXRyaWV2ZSByZWxldmFudCBtZW1vcmllc1xyXG4gICAgbGV0IG1lbW9yaWVzU2VjdGlvbiA9ICdObyByZWxldmFudCBtZW1vcmllcyB5ZXQuJztcclxuICAgIGlmIChtZW1vcnlSZXRyaWV2ZXIgJiYgY2hhcHRlck51bWJlciA+IDEpIHtcclxuICAgICAgY29uc3QgbWVtb3JpZXMgPSBhd2FpdCBtZW1vcnlSZXRyaWV2ZXIucmV0cmlldmVGb3JDaGFwdGVyKHtcclxuICAgICAgICBiaWJsZSxcclxuICAgICAgICBzdGF0ZSxcclxuICAgICAgICBjdXJyZW50Q2hhcHRlcjogY2hhcHRlck51bWJlcixcclxuICAgICAgfSk7XHJcbiAgICAgIG1lbW9yaWVzU2VjdGlvbiA9IG1lbW9yeVJldHJpZXZlci5mb3JtYXRNZW1vcmllc0ZvclByb21wdChtZW1vcmllcykgfHwgJ05vIGhpZ2hseSByZWxldmFudCBtZW1vcmllcyBmb3IgdGhpcyBjaGFwdGVyLic7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgcmVjZW50U3VtbWFyaWVzID0gc3RhdGUuY2hhcHRlclN1bW1hcmllc1xyXG4gICAgICAuc2xpY2UoLTMpXHJcbiAgICAgIC5tYXAocyA9PiBgQ2hhcHRlciAke3MuY2hhcHRlck51bWJlcn06ICR7cy5zdW1tYXJ5fWApXHJcbiAgICAgIC5qb2luKCdcXG5cXG4nKSB8fCAnVGhpcyBpcyB0aGUgZmlyc3QgY2hhcHRlci4nO1xyXG5cclxuICAgIGNvbnN0IGNoYXJhY3RlcnMgPSBiaWJsZS5jaGFyYWN0ZXJzXHJcbiAgICAgIC5tYXAoYyA9PiBgLSAke2MubmFtZX0gKCR7Yy5yb2xlfSk6ICR7Yy5wZXJzb25hbGl0eS5qb2luKCcsICcpfS4gR29hbHM6ICR7Yy5nb2Fscy5qb2luKCcsICcpfWApXHJcbiAgICAgIC5qb2luKCdcXG4nKTtcclxuXHJcbiAgICBjb25zdCBjaGFwdGVyR29hbCA9IHRoaXMuaW5mZXJDaGFwdGVyR29hbChiaWJsZSwgc3RhdGUsIGNoYXB0ZXJOdW1iZXIpO1xyXG5cclxuICAgIGNvbnN0IHByb21wdCA9IHRoaXMucHJvbXB0VGVtcGxhdGVcclxuICAgICAgLnJlcGxhY2UoJ3t7Y2hhcHRlck51bWJlcn19JywgY2hhcHRlck51bWJlci50b1N0cmluZygpKVxyXG4gICAgICAucmVwbGFjZSgne3t0aXRsZX19JywgYmlibGUudGl0bGUpXHJcbiAgICAgIC5yZXBsYWNlKCd7e3RoZW1lfX0nLCBiaWJsZS50aGVtZSlcclxuICAgICAgLnJlcGxhY2UoJ3t7Z2VucmV9fScsIGJpYmxlLmdlbnJlKVxyXG4gICAgICAucmVwbGFjZSgne3tzZXR0aW5nfX0nLCBiaWJsZS5zZXR0aW5nKVxyXG4gICAgICAucmVwbGFjZSgne3t0b25lfX0nLCBiaWJsZS50b25lKVxyXG4gICAgICAucmVwbGFjZSgne3twcmVtaXNlfX0nLCBiaWJsZS5wcmVtaXNlKVxyXG4gICAgICAucmVwbGFjZSgne3tjaGFyYWN0ZXJzfX0nLCBjaGFyYWN0ZXJzKVxyXG4gICAgICAucmVwbGFjZSgne3ttZW1vcmllc319JywgbWVtb3JpZXNTZWN0aW9uKVxyXG4gICAgICAucmVwbGFjZSgne3tyZWNlbnRTdW1tYXJpZXN9fScsIHJlY2VudFN1bW1hcmllcylcclxuICAgICAgLnJlcGxhY2UoJ3t7Y2hhcHRlckdvYWx9fScsIGNoYXB0ZXJHb2FsKVxyXG4gICAgICAucmVwbGFjZSgne3t0YXJnZXRXb3JkQ291bnR9fScsIHRhcmdldFdvcmRDb3VudC50b1N0cmluZygpKVxyXG4gICAgICAucmVwbGFjZSgne3tjYW5vbn19JywgY2Fub25TZWN0aW9uKTtcclxuXHJcbiAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgZ2V0TExNKCkuY29tcGxldGUocHJvbXB0LCB7XHJcbiAgICAgIHRlbXBlcmF0dXJlOiAwLjgsXHJcbiAgICAgIG1heFRva2VuczogNDAwMCxcclxuICAgIH0pO1xyXG5cclxuICAgIGNvbnN0IHRpdGxlID0gdGhpcy5leHRyYWN0VGl0bGUoY29udGVudCkgfHwgYENoYXB0ZXIgJHtjaGFwdGVyTnVtYmVyfWA7XHJcbiAgICBjb25zdCB3b3JkQ291bnQgPSBjb250ZW50LnNwbGl0KC9cXHMrLykubGVuZ3RoO1xyXG5cclxuICAgIHJldHVybiB7IGNvbnRlbnQsIHRpdGxlLCB3b3JkQ291bnQgfTtcclxuICB9XHJcblxyXG4gIGFzeW5jIGNvbnRpbnVlKGV4aXN0aW5nQ29udGVudDogc3RyaW5nLCBjb250ZXh0OiBHZW5lcmF0aW9uQ29udGV4dCk6IFByb21pc2U8c3RyaW5nPiB7XHJcbiAgICBjb25zdCBwcm9tcHQgPSBgQ29udGludWUgdGhlIGZvbGxvd2luZyBjaGFwdGVyIGZyb20gd2hlcmUgaXQgbGVmdCBvZmYuIERvIG5vdCByZXBlYXQgd2hhdCBoYXMgYWxyZWFkeSBiZWVuIHdyaXR0ZW4uIFdyaXRlIHRoZSBjb250aW51YXRpb24gb25seS5cclxuXHJcbiMjIFN0b3J5IENvbnRleHRcclxuXHJcbiR7Y29udGV4dC5iaWJsZS5wcmVtaXNlfVxyXG5cclxuIyMgRXhpc3RpbmcgQ2hhcHRlciBDb250ZW50XHJcblxyXG4ke2V4aXN0aW5nQ29udGVudH1cclxuXHJcbiMjIENvbnRpbnVhdGlvblxyXG5cclxuQ29udGludWUgbmF0dXJhbGx5IGZyb20gdGhlIGxhc3Qgc2VudGVuY2U6YDtcclxuXHJcbiAgICBjb25zdCBjb250aW51YXRpb24gPSBhd2FpdCBnZXRMTE0oKS5jb21wbGV0ZShwcm9tcHQsIHtcclxuICAgICAgdGVtcGVyYXR1cmU6IDAuOCxcclxuICAgICAgbWF4VG9rZW5zOiAyMDAwLFxyXG4gICAgfSk7XHJcblxyXG4gICAgcmV0dXJuIGV4aXN0aW5nQ29udGVudCArICdcXG5cXG4nICsgY29udGludWF0aW9uO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBpbmZlckNoYXB0ZXJHb2FsKGJpYmxlOiBHZW5lcmF0aW9uQ29udGV4dFsnYmlibGUnXSwgc3RhdGU6IEdlbmVyYXRpb25Db250ZXh0WydzdGF0ZSddLCBjaGFwdGVyTnVtYmVyOiBudW1iZXIpOiBzdHJpbmcge1xyXG4gICAgY29uc3QgcHJvZ3Jlc3MgPSBjaGFwdGVyTnVtYmVyIC8gYmlibGUudGFyZ2V0Q2hhcHRlcnM7XHJcbiAgICBcclxuICAgIGlmIChwcm9ncmVzcyA8IDAuMikge1xyXG4gICAgICByZXR1cm4gJ0VzdGFibGlzaCB0aGUgc2V0dGluZywgaW50cm9kdWNlIHRoZSBtYWluIGNoYXJhY3RlciwgYW5kIGhpbnQgYXQgdGhlIGNlbnRyYWwgY29uZmxpY3QuJztcclxuICAgIH0gZWxzZSBpZiAocHJvZ3Jlc3MgPCAwLjUpIHtcclxuICAgICAgcmV0dXJuICdEZXZlbG9wIHRoZSBjb25mbGljdCwgaW50cm9kdWNlIGNvbXBsaWNhdGlvbnMsIGFuZCByYWlzZSBzdGFrZXMuJztcclxuICAgIH0gZWxzZSBpZiAocHJvZ3Jlc3MgPCAwLjgpIHtcclxuICAgICAgcmV0dXJuICdFc2NhbGF0ZSB0ZW5zaW9uLCByZXZlYWwgaW1wb3J0YW50IGluZm9ybWF0aW9uLCBhbmQgbW92ZSB0b3dhcmQgY2xpbWF4Lic7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICByZXR1cm4gJ0J1aWxkIHRvd2FyZCByZXNvbHV0aW9uIGFuZCBiZWdpbiB0eWluZyB1cCBwbG90IHRocmVhZHMuJztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHByaXZhdGUgZXh0cmFjdFRpdGxlKGNvbnRlbnQ6IHN0cmluZyk6IHN0cmluZyB8IG51bGwge1xyXG4gICAgY29uc3QgbGluZXMgPSBjb250ZW50LnNwbGl0KCdcXG4nKTtcclxuICAgIGZvciAoY29uc3QgbGluZSBvZiBsaW5lcy5zbGljZSgwLCAxMCkpIHtcclxuICAgICAgY29uc3QgdHJpbW1lZCA9IGxpbmUudHJpbSgpO1xyXG4gICAgICBpZiAodHJpbW1lZC5zdGFydHNXaXRoKCcjICcpIHx8IHRyaW1tZWQuc3RhcnRzV2l0aCgnQ2hhcHRlcicpKSB7XHJcbiAgICAgICAgcmV0dXJuIHRyaW1tZWQucmVwbGFjZSgvXiNcXHMqLywgJycpLnRyaW0oKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgY29uc3Qgd3JpdGVyID0gbmV3IENoYXB0ZXJXcml0ZXIoKTtcclxuIl19
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { CharacterState } from '../story/structuredState.js';
|
|
2
|
+
export type NodeType = 'character' | 'location' | 'fact' | 'event' | 'item';
|
|
3
|
+
export interface ConstraintNode {
|
|
4
|
+
id: string;
|
|
5
|
+
type: NodeType;
|
|
6
|
+
label: string;
|
|
7
|
+
properties: Record<string, any>;
|
|
8
|
+
chapterEstablished?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ConstraintEdge {
|
|
11
|
+
id: string;
|
|
12
|
+
from: string;
|
|
13
|
+
to: string;
|
|
14
|
+
type: string;
|
|
15
|
+
properties: Record<string, any>;
|
|
16
|
+
}
|
|
17
|
+
export interface ConstraintViolation {
|
|
18
|
+
type: 'canon' | 'location' | 'knowledge' | 'timeline' | 'logic';
|
|
19
|
+
severity: 'error' | 'warning';
|
|
20
|
+
description: string;
|
|
21
|
+
nodes: string[];
|
|
22
|
+
suggestedFix?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare class ConstraintGraph {
|
|
25
|
+
private nodes;
|
|
26
|
+
private edges;
|
|
27
|
+
private adjacencyList;
|
|
28
|
+
/**
|
|
29
|
+
* Add a node to the graph
|
|
30
|
+
*/
|
|
31
|
+
addNode(node: ConstraintNode): void;
|
|
32
|
+
/**
|
|
33
|
+
* Add an edge between nodes
|
|
34
|
+
*/
|
|
35
|
+
addEdge(edge: ConstraintEdge): void;
|
|
36
|
+
/**
|
|
37
|
+
* Get a node by ID
|
|
38
|
+
*/
|
|
39
|
+
getNode(id: string): ConstraintNode | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Get all edges from a node
|
|
42
|
+
*/
|
|
43
|
+
getEdgesFrom(nodeId: string): ConstraintEdge[];
|
|
44
|
+
/**
|
|
45
|
+
* Get all edges to a node
|
|
46
|
+
*/
|
|
47
|
+
getEdgesTo(nodeId: string): ConstraintEdge[];
|
|
48
|
+
/**
|
|
49
|
+
* Get neighbors of a node
|
|
50
|
+
*/
|
|
51
|
+
getNeighbors(nodeId: string): ConstraintNode[];
|
|
52
|
+
/**
|
|
53
|
+
* Add character to graph
|
|
54
|
+
*/
|
|
55
|
+
addCharacter(character: CharacterState, chapter: number): void;
|
|
56
|
+
/**
|
|
57
|
+
* Add location to graph
|
|
58
|
+
*/
|
|
59
|
+
addLocation(name: string, description: string, chapter: number): void;
|
|
60
|
+
/**
|
|
61
|
+
* Add event to graph
|
|
62
|
+
*/
|
|
63
|
+
addEvent(id: string, description: string, participants: string[], chapter: number): void;
|
|
64
|
+
/**
|
|
65
|
+
* Update character location
|
|
66
|
+
*/
|
|
67
|
+
updateCharacterLocation(characterName: string, newLocation: string, chapter: number): void;
|
|
68
|
+
/**
|
|
69
|
+
* Check for constraint violations
|
|
70
|
+
*/
|
|
71
|
+
checkConstraints(currentChapter: number): ConstraintViolation[];
|
|
72
|
+
/**
|
|
73
|
+
* Check location consistency (no teleporting)
|
|
74
|
+
*/
|
|
75
|
+
private checkLocationConsistency;
|
|
76
|
+
/**
|
|
77
|
+
* Check knowledge consistency (no impossible knowledge)
|
|
78
|
+
*/
|
|
79
|
+
private checkKnowledgeConsistency;
|
|
80
|
+
/**
|
|
81
|
+
* Check timeline consistency
|
|
82
|
+
*/
|
|
83
|
+
private checkTimelineConsistency;
|
|
84
|
+
/**
|
|
85
|
+
* Check logical consistency
|
|
86
|
+
*/
|
|
87
|
+
private checkLogicalConsistency;
|
|
88
|
+
/**
|
|
89
|
+
* Query what a character knows
|
|
90
|
+
*/
|
|
91
|
+
getCharacterKnowledge(characterName: string): ConstraintNode[];
|
|
92
|
+
/**
|
|
93
|
+
* Query where a character is
|
|
94
|
+
*/
|
|
95
|
+
getCharacterLocation(characterName: string): string | undefined;
|
|
96
|
+
/**
|
|
97
|
+
* Serialize graph
|
|
98
|
+
*/
|
|
99
|
+
serialize(): string;
|
|
100
|
+
/**
|
|
101
|
+
* Load graph from serialized data
|
|
102
|
+
*/
|
|
103
|
+
load(data: string): void;
|
|
104
|
+
/**
|
|
105
|
+
* Get graph statistics
|
|
106
|
+
*/
|
|
107
|
+
getStats(): {
|
|
108
|
+
nodes: number;
|
|
109
|
+
edges: number;
|
|
110
|
+
byType: Record<NodeType, number>;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Sanitize string for use as ID
|
|
114
|
+
*/
|
|
115
|
+
private sanitizeId;
|
|
116
|
+
}
|
|
117
|
+
export declare function createConstraintGraph(): ConstraintGraph;
|