@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,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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuc2lvbkNvbnRyb2xsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnRzL3RlbnNpb25Db250cm9sbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQTJCQSx3REFlQztBQUtELGtFQUtDO0FBS0Qsd0NBdUNDO0FBS0QsMERBK0NDO0FBS0Qsd0RBYUM7QUFNRCxnRUFvQ0M7QUE5TEQ7Ozs7Ozs7O0dBUUc7QUFDSCxTQUFnQixzQkFBc0IsQ0FDcEMsY0FBc0IsRUFDdEIsYUFBcUI7SUFFckIsSUFBSSxhQUFhLElBQUksQ0FBQztRQUFFLE9BQU8sR0FBRyxDQUFDO0lBRW5DLHVCQUF1QjtJQUN2QixNQUFNLFFBQVEsR0FBRyxDQUFDLGNBQWMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLGFBQWEsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUU1RCxtQ0FBbUM7SUFDbkMsZ0NBQWdDO0lBQ2hDLE1BQU0sYUFBYSxHQUFHLENBQUMsR0FBRyxRQUFRLEdBQUcsQ0FBQyxDQUFDLEdBQUcsUUFBUSxDQUFDLENBQUM7SUFFcEQsNEJBQTRCO0lBQzVCLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxhQUFhLEdBQUcsR0FBRyxDQUFDLEdBQUcsR0FBRyxDQUFDO0FBQy9DLENBQUM7QUFFRDs7R0FFRztBQUNILFNBQWdCLDJCQUEyQixDQUN6QyxjQUFzQixFQUN0QixhQUFxQjtJQUVyQixPQUFPLHNCQUFzQixDQUFDLGNBQWMsR0FBRyxDQUFDLEVBQUUsYUFBYSxDQUFDLENBQUM7QUFDbkUsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsY0FBYyxDQUM1QixVQUFzQixFQUN0QixlQUFxQztJQUVyQyxNQUFNLGFBQWEsR0FBRyxzQkFBc0IsQ0FDMUMsVUFBVSxDQUFDLGNBQWMsRUFDekIsVUFBVSxDQUFDLGFBQWEsQ0FDekIsQ0FBQztJQUVGLE1BQU0sY0FBYyxHQUFHLGVBQWUsQ0FBQyxPQUFPLENBQUM7SUFDL0MsTUFBTSxVQUFVLEdBQUcsYUFBYSxHQUFHLGNBQWMsQ0FBQztJQUVsRCxJQUFJLGlCQUF1RCxDQUFDO0lBQzVELElBQUksU0FBaUIsQ0FBQztJQUV0QixJQUFJLFVBQVUsQ0FBQyxjQUFjLEtBQUssVUFBVSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQzNELGlCQUFpQixHQUFHLFNBQVMsQ0FBQztRQUM5QixTQUFTLEdBQUcsOENBQThDLENBQUM7SUFDN0QsQ0FBQztTQUFNLElBQUksVUFBVSxHQUFHLEdBQUcsRUFBRSxDQUFDO1FBQzVCLGlCQUFpQixHQUFHLFVBQVUsQ0FBQztRQUMvQixTQUFTLEdBQUcsY0FBYyxDQUFDLFVBQVUsR0FBRyxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLG9DQUFvQyxDQUFDO0lBQzlGLENBQUM7U0FBTSxJQUFJLFVBQVUsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzlCLGlCQUFpQixHQUFHLFVBQVUsQ0FBQztRQUMvQixTQUFTLEdBQUcsY0FBYyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyx1Q0FBdUMsQ0FBQztJQUMzRyxDQUFDO1NBQU0sSUFBSSxhQUFhLEdBQUcsSUFBSSxFQUFFLENBQUM7UUFDaEMsaUJBQWlCLEdBQUcsUUFBUSxDQUFDO1FBQzdCLFNBQVMsR0FBRyx5Q0FBeUMsQ0FBQztJQUN4RCxDQUFDO1NBQU0sQ0FBQztRQUNOLGlCQUFpQixHQUFHLFVBQVUsQ0FBQztRQUMvQixTQUFTLEdBQUcscUNBQXFDLENBQUM7SUFDcEQsQ0FBQztJQUVELE9BQU87UUFDTCxjQUFjO1FBQ2QsYUFBYTtRQUNiLFVBQVU7UUFDVixpQkFBaUI7UUFDakIsU0FBUztLQUNWLENBQUM7QUFDSixDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFnQix1QkFBdUIsQ0FDckMsUUFBeUIsRUFDekIsVUFBc0I7SUFFdEIsTUFBTSxFQUFFLGFBQWEsRUFBRSxpQkFBaUIsRUFBRSxHQUFHLFFBQVEsQ0FBQztJQUV0RCxJQUFJLFFBQWdCLENBQUM7SUFDckIsSUFBSSxVQUFvQixDQUFDO0lBQ3pCLElBQUksV0FBbUIsQ0FBQztJQUV4QixRQUFRLGlCQUFpQixFQUFFLENBQUM7UUFDMUIsS0FBSyxVQUFVO1lBQ2IsUUFBUSxHQUFHLHdGQUF3RixDQUFDO1lBQ3BHLFVBQVUsR0FBRyxDQUFDLGVBQWUsRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQ2pFLFdBQVcsR0FBRyxtREFBbUQsQ0FBQztZQUNsRSxNQUFNO1FBRVIsS0FBSyxVQUFVO1lBQ2IsUUFBUSxHQUFHLHdFQUF3RSxDQUFDO1lBQ3BGLFVBQVUsR0FBRyxDQUFDLGFBQWEsRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLFlBQVksQ0FBQyxDQUFDO1lBQ3pFLFdBQVcsR0FBRyxnREFBZ0QsQ0FBQztZQUMvRCxNQUFNO1FBRVIsS0FBSyxTQUFTO1lBQ1osUUFBUSxHQUFHLDZFQUE2RSxDQUFDO1lBQ3pGLFVBQVUsR0FBRyxDQUFDLFlBQVksRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLGVBQWUsQ0FBQyxDQUFDO1lBQ3ZFLFdBQVcsR0FBRyw4Q0FBOEMsQ0FBQztZQUM3RCxNQUFNO1FBRVIsS0FBSyxRQUFRO1lBQ1gsUUFBUSxHQUFHLGdGQUFnRixDQUFDO1lBQzVGLFVBQVUsR0FBRyxDQUFDLFFBQVEsRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLFdBQVcsQ0FBQyxDQUFDO1lBQy9ELFdBQVcsR0FBRyxnREFBZ0QsQ0FBQztZQUMvRCxNQUFNO1FBRVI7WUFDRSxRQUFRLEdBQUcsdUNBQXVDLENBQUM7WUFDbkQsVUFBVSxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDdkIsV0FBVyxHQUFHLGlCQUFpQixDQUFDO0lBQ3BDLENBQUM7SUFFRCxPQUFPO1FBQ0wsYUFBYTtRQUNiLFFBQVE7UUFDUixVQUFVO1FBQ1YsV0FBVztLQUNaLENBQUM7QUFDSixDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFnQixzQkFBc0IsQ0FBQyxRQUF5QjtJQUM5RCxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxhQUFhLEdBQUcsR0FBRyxDQUFDLENBQUM7SUFFaEUsT0FBTzs7NEJBRW1CLGNBQWM7O2dCQUUxQixRQUFRLENBQUMsUUFBUTs7K0JBRUYsUUFBUSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDOztvQkFFekMsUUFBUSxDQUFDLFdBQVc7Q0FDdkMsQ0FBQztBQUNGLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUFnQiwwQkFBMEIsQ0FBQyxPQUFnQjtJQUN6RCxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBRTlDLHFCQUFxQjtJQUNyQixNQUFNLFlBQVksR0FBRztRQUNuQixNQUFNLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFFBQVE7UUFDdkQsUUFBUSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFFBQVE7UUFDNUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLE1BQU07UUFDeEMsVUFBVSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE9BQU87UUFDbEQsVUFBVSxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLFNBQVM7UUFDakQsT0FBTyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLFFBQVE7UUFDaEQsUUFBUSxFQUFFLFdBQVcsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLFdBQVc7S0FDekQsQ0FBQztJQUVGLE1BQU0sU0FBUyxHQUFHO1FBQ2hCLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNO1FBQ3pDLE9BQU8sRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxTQUFTO1FBQzNDLE1BQU0sRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxRQUFRO0tBQ25ELENBQUM7SUFFRixJQUFJLFlBQVksR0FBRyxHQUFHLENBQUMsQ0FBQyxXQUFXO0lBRW5DLDJCQUEyQjtJQUMzQixLQUFLLE1BQU0sSUFBSSxJQUFJLFlBQVksRUFBRSxDQUFDO1FBQ2hDLE1BQU0sS0FBSyxHQUFHLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFDbEUsWUFBWSxJQUFJLEtBQUssR0FBRyxJQUFJLENBQUM7SUFDL0IsQ0FBQztJQUVELDJCQUEyQjtJQUMzQixLQUFLLE1BQU0sSUFBSSxJQUFJLFNBQVMsRUFBRSxDQUFDO1FBQzdCLE1BQU0sS0FBSyxHQUFHLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFDbEUsWUFBWSxJQUFJLEtBQUssR0FBRyxLQUFLLENBQUM7SUFDaEMsQ0FBQztJQUVELHdCQUF3QjtJQUN4QixPQUFPLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxHQUFHLEdBQUcsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUM7QUFDeEUsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBYSxpQkFBaUI7SUFDNUI7O09BRUc7SUFDSCxPQUFPLENBQ0wsVUFBc0IsRUFDdEIsZUFBcUM7UUFFckMsT0FBTyxjQUFjLENBQUMsVUFBVSxFQUFFLGVBQWUsQ0FBQyxDQUFDO0lBQ3JELENBQUM7SUFFRDs7T0FFRztJQUNILGdCQUFnQixDQUNkLFVBQXNCLEVBQ3RCLGVBQXFDO1FBRXJDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBQzNELE9BQU8sdUJBQXVCLENBQUMsUUFBUSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQ3ZELENBQUM7SUFFRDs7T0FFRztJQUNILGVBQWUsQ0FBQyxhQUFxQixFQUFFLGFBQXFCO1FBQzFELE9BQU8sc0JBQXNCLENBQUMsYUFBYSxFQUFFLGFBQWEsQ0FBQyxDQUFDO0lBQzlELENBQUM7SUFFRDs7T0FFRztJQUNILG1CQUFtQixDQUFDLE9BQWdCO1FBQ2xDLE9BQU8sMEJBQTBCLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDN0MsQ0FBQztDQUNGO0FBbkNELDhDQW1DQztBQUVZLFFBQUEsaUJBQWlCLEdBQUcsSUFBSSxpQkFBaUIsRUFBRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBDaGFwdGVyLCBTdG9yeUJpYmxlLCBTdG9yeVN0YXRlIH0gZnJvbSAnLi4vdHlwZXMvaW5kZXguanMnO1xyXG5pbXBvcnQgdHlwZSB7IFN0b3J5U3RydWN0dXJlZFN0YXRlIH0gZnJvbSAnLi4vc3Rvcnkvc3RydWN0dXJlZFN0YXRlLmpzJztcclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgVGVuc2lvbkFuYWx5c2lzIHtcclxuICBjdXJyZW50VGVuc2lvbjogbnVtYmVyO1xyXG4gIHRhcmdldFRlbnNpb246IG51bWJlcjtcclxuICB0ZW5zaW9uR2FwOiBudW1iZXI7IC8vIHBvc2l0aXZlID0gbmVlZCB0byBpbmNyZWFzZSwgbmVnYXRpdmUgPSBuZWVkIHRvIGRlY3JlYXNlXHJcbiAgcmVjb21tZW5kZWRBY3Rpb246ICdlc2NhbGF0ZScgfCAnbWFpbnRhaW4nIHwgJ3Jlc29sdmUnIHwgJ2NsaW1heCc7XHJcbiAgcmVhc29uaW5nOiBzdHJpbmc7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgVGVuc2lvbkd1aWRhbmNlIHtcclxuICB0YXJnZXRUZW5zaW9uOiBudW1iZXI7XHJcbiAgZ3VpZGFuY2U6IHN0cmluZztcclxuICBzY2VuZVR5cGVzOiBzdHJpbmdbXTtcclxuICBwYWNpbmdOb3Rlczogc3RyaW5nO1xyXG59XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlIHRhcmdldCB0ZW5zaW9uIHVzaW5nIHBhcmFib2xpYyBjdXJ2ZVxyXG4gKiBGb3JtdWxhOiB0YXJnZXRUZW5zaW9uID0gNCAqIHByb2dyZXNzICogKDEgLSBwcm9ncmVzcylcclxuICogXHJcbiAqIFRoaXMgY3JlYXRlcyBhIG5hdHVyYWwgZHJhbWF0aWMgYXJjOlxyXG4gKiAtIENoYXB0ZXIgMDogMCUgdGVuc2lvbiAoc2V0dXApXHJcbiAqIC0gTWlkZGxlIGNoYXB0ZXJzOiB+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;