@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,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stateUpdater = exports.StateUpdater = void 0;
4
+ const client_js_1 = require("../llm/client.js");
5
+ const STATE_UPDATE_PROMPT = `You are a narrative state tracker. Analyze the chapter and extract state changes.
6
+
7
+ ## Story Bible
8
+
9
+ **Title:** {{title}}
10
+ **Genre:** {{genre}}
11
+
12
+ ## Characters
13
+
14
+ {{characters}}
15
+
16
+ ## Current Plot Threads
17
+
18
+ {{plotThreads}}
19
+
20
+ ## Chapter Content
21
+
22
+ **Chapter {{chapterNumber}}: {{chapterTitle}}**
23
+
24
+ {{chapterContent}}
25
+
26
+ ## Current Unresolved Questions
27
+
28
+ {{unresolvedQuestions}}
29
+
30
+ ## Task
31
+
32
+ Analyze what changed in this chapter. Output JSON with:
33
+
34
+ 1. **characterUpdates**: How characters changed (emotion, location, knowledge, relationships)
35
+ 2. **plotThreadUpdates**: How plot threads progressed (status, tension)
36
+ 3. **newQuestions**: New mysteries or questions raised
37
+ 4. **resolvedQuestions**: Which current questions were answered
38
+ 5. **recentEvents**: Key events that happened (2-3 bullet points)
39
+
40
+ Example output:
41
+ {
42
+ "characterUpdates": [
43
+ {
44
+ "name": "Alice",
45
+ "emotionalState": "anxious",
46
+ "location": "the abandoned warehouse",
47
+ "newKnowledge": ["the code is 8472"],
48
+ "relationshipChanges": [{"with": "Bob", "status": "distrustful"}],
49
+ "development": "Alice realizes she can't trust her mentor"
50
+ }
51
+ ],
52
+ "plotThreadUpdates": [
53
+ {
54
+ "id": "missing_brother",
55
+ "status": "escalating",
56
+ "tensionChange": 0.1,
57
+ "summary": "Discovered brother was kidnapped by the syndicate"
58
+ }
59
+ ],
60
+ "newQuestions": ["Who is the mysterious informant?"],
61
+ "resolvedQuestions": ["Where was the brother taken?"],
62
+ "recentEvents": ["Alice broke into the warehouse", "Found the hidden room"]
63
+ }`;
64
+ class StateUpdater {
65
+ async extractStateChanges(chapter, bible, currentState) {
66
+ const characters = Object.values(currentState.characters)
67
+ .map(c => `- ${c.name}: currently ${c.emotionalState}, at ${c.location}`)
68
+ .join('\n') || 'No characters tracked yet.';
69
+ const plotThreads = Object.values(currentState.plotThreads)
70
+ .map(t => `- ${t.name} (${t.status}, tension: ${Math.round(t.tension * 100)}%): ${t.summary}`)
71
+ .join('\n') || 'No active plot threads.';
72
+ const unresolvedQuestions = currentState.unresolvedQuestions.length > 0
73
+ ? currentState.unresolvedQuestions.map(q => `- ${q}`).join('\n')
74
+ : 'None';
75
+ const prompt = STATE_UPDATE_PROMPT
76
+ .replace('{{title}}', bible.title)
77
+ .replace('{{genre}}', bible.genre)
78
+ .replace('{{characters}}', characters)
79
+ .replace('{{plotThreads}}', plotThreads)
80
+ .replace('{{chapterNumber}}', chapter.number.toString())
81
+ .replace('{{chapterTitle}}', chapter.title)
82
+ .replace('{{chapterContent}}', chapter.content.substring(0, 6000))
83
+ .replace('{{unresolvedQuestions}}', unresolvedQuestions);
84
+ const result = await (0, client_js_1.getLLM)().completeJSON(prompt, {
85
+ temperature: 0.3,
86
+ maxTokens: 2000,
87
+ });
88
+ return result;
89
+ }
90
+ applyUpdates(state, updates, chapterNumber) {
91
+ let newState = { ...state };
92
+ // Apply character updates
93
+ for (const update of updates.characterUpdates) {
94
+ if (newState.characters[update.name]) {
95
+ const char = newState.characters[update.name];
96
+ if (update.emotionalState) {
97
+ char.emotionalState = update.emotionalState;
98
+ }
99
+ if (update.location) {
100
+ char.location = update.location;
101
+ }
102
+ if (update.newKnowledge) {
103
+ char.knowledge = [...char.knowledge, ...update.newKnowledge];
104
+ }
105
+ if (update.relationshipChanges) {
106
+ for (const rel of update.relationshipChanges) {
107
+ char.relationships[rel.with] = rel.status;
108
+ }
109
+ }
110
+ if (update.development) {
111
+ char.development = [...char.development, update.development];
112
+ }
113
+ }
114
+ }
115
+ // Apply plot thread updates
116
+ for (const update of updates.plotThreadUpdates) {
117
+ if (newState.plotThreads[update.id]) {
118
+ const thread = newState.plotThreads[update.id];
119
+ if (update.status) {
120
+ thread.status = update.status;
121
+ }
122
+ if (update.tensionChange !== undefined) {
123
+ thread.tension = Math.max(0, Math.min(1, thread.tension + update.tensionChange));
124
+ }
125
+ if (update.summary) {
126
+ thread.summary = update.summary;
127
+ }
128
+ thread.lastChapter = chapterNumber;
129
+ }
130
+ }
131
+ // Add new questions
132
+ for (const question of updates.newQuestions) {
133
+ if (!newState.unresolvedQuestions.includes(question)) {
134
+ newState.unresolvedQuestions = [...newState.unresolvedQuestions, question];
135
+ }
136
+ }
137
+ // Remove resolved questions
138
+ for (const question of updates.resolvedQuestions) {
139
+ newState.unresolvedQuestions = newState.unresolvedQuestions.filter(q => q !== question);
140
+ }
141
+ // Add recent events
142
+ for (const event of updates.recentEvents) {
143
+ newState.recentEvents = [...newState.recentEvents, event].slice(-10);
144
+ }
145
+ return newState;
146
+ }
147
+ }
148
+ exports.StateUpdater = StateUpdater;
149
+ exports.stateUpdater = new StateUpdater();
150
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGVVcGRhdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FnZW50cy9zdGF0ZVVwZGF0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsZ0RBQTBDO0FBd0IxQyxNQUFNLG1CQUFtQixHQUFHOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0VBMEQxQixDQUFDO0FBRUgsTUFBYSxZQUFZO0lBQ3ZCLEtBQUssQ0FBQyxtQkFBbUIsQ0FDdkIsT0FBZ0IsRUFDaEIsS0FBaUIsRUFDakIsWUFBa0M7UUFFbEMsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDO2FBQ3RELEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksZUFBZSxDQUFDLENBQUMsY0FBYyxRQUFRLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQzthQUN4RSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksNEJBQTRCLENBQUM7UUFFOUMsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsV0FBVyxDQUFDO2FBQ3hELEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLENBQUMsTUFBTSxjQUFjLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLE9BQU8sR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7YUFDN0YsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLHlCQUF5QixDQUFDO1FBRTNDLE1BQU0sbUJBQW1CLEdBQUcsWUFBWSxDQUFDLG1CQUFtQixDQUFDLE1BQU0sR0FBRyxDQUFDO1lBQ3JFLENBQUMsQ0FBQyxZQUFZLENBQUMsbUJBQW1CLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxF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SSxPQUFPLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDekMsUUFBUSxDQUFDLFlBQVksR0FBRyxDQUFDLEdBQUcsUUFBUSxDQUFDLFlBQVksRUFBRSxLQUFLLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN2RSxDQUFDO1FBRUQsT0FBTyxRQUFRLENBQUM7SUFDbEIsQ0FBQztDQUNGO0FBekdELG9DQXlHQztBQUVZLFFBQUEsWUFBWSxHQUFHLElBQUksWUFBWSxFQUFFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBnZXRMTE0gfSBmcm9tICcuLi9sbG0vY2xpZW50LmpzJztcclxuaW1wb3J0IHR5cGUgeyBDaGFwdGVyLCBTdG9yeUJpYmxlIH0gZnJvbSAnLi4vdHlwZXMvaW5kZXguanMnO1xyXG5pbXBvcnQgdHlwZSB7IFN0b3J5U3RydWN0dXJlZFN0YXRlLCBDaGFyYWN0ZXJTdGF0ZSwgUGxvdFRocmVhZFN0YXRlIH0gZnJvbSAnLi4vc3Rvcnkvc3RydWN0dXJlZFN0YXRlLmpzJztcclxuXHJcbmludGVyZmFjZSBTdGF0ZVVwZGF0ZU91dHB1dCB7XHJcbiAgY2hhcmFjdGVyVXBkYXRlczogQXJyYXk8e1xyXG4gICAgbmFtZTogc3RyaW5nO1xyXG4gICAgZW1vdGlvbmFsU3RhdGU/OiBzdHJpbmc7XHJcbiAgICBsb2NhdGlvbj86IHN0cmluZztcclxuICAgIG5ld0tub3dsZWRnZT86IHN0cmluZ1tdO1xyXG4gICAgcmVsYXRpb25zaGlwQ2hhbmdlcz86IEFycmF5PHsgd2l0aDogc3RyaW5nOyBzdGF0dXM6IHN0cmluZyB9PjtcclxuICAgIGRldmVsb3BtZW50Pzogc3RyaW5nO1xyXG4gIH0+O1xyXG4gIHBsb3RUaHJlYWRVcGRhdGVzOiBBcnJheTx7XHJcbiAgICBpZDogc3RyaW5nO1xyXG4gICAgc3RhdHVzPzogJ2Rvcm1hbnQnIHwgJ2FjdGl2ZScgfCAnZXNjYWxhdGluZycgfCAncmVzb2x2ZWQnO1xyXG4gICAgdGVuc2lvbkNoYW5nZT86IG51bWJlcjsgLy8gLTAuMSB0byArMC4xXHJcbiAgICBzdW1tYXJ5Pzogc3RyaW5nO1xyXG4gIH0+O1xyXG4gIG5ld1F1ZXN0aW9uczogc3RyaW5nW107XHJcbiAgcmVzb2x2ZWRRdWVzdGlvbnM6IHN0cmluZ1tdO1xyXG4gIHJlY2VudEV2ZW50czogc3RyaW5nW107XHJcbn1cclxuXHJcbmNvbnN0IFNUQVRFX1VQREFURV9QUk9NUFQgPSBgWW91IGFyZSBhIG5hcnJhdGl2ZSBzdGF0ZSB0cmFja2VyLiBBbmFseXplIHRoZSBjaGFwdGVyIGFuZCBleHRyYWN0IHN0YXRlIGNoYW5nZXMuXHJcblxyXG4jIyBTdG9yeSBCaWJsZVxyXG5cclxuKipUaXRsZToqKiB7e3RpdGxlfX1cclxuKipHZW5yZToqKiB7e2dlbnJlfX1cclxuXHJcbiMjIENoYXJhY3RlcnNcclxuXHJcbnt7Y2hhcmFjdGVyc319XHJcblxyXG4jIyBDdXJyZW50IFBsb3QgVGhyZWFkc1xyXG5cclxue3twbG90VGhyZWFkc319XHJcblxyXG4jIyBDaGFwdGVyIENvbnRlbnRcclxuXHJcbioqQ2hhcHRlciB7e2NoYXB0ZXJOdW1iZXJ9fToge3tjaGFwdGVyVGl0bGV9fSoqXHJcblxyXG57e2NoYXB0ZXJDb250ZW50fX1cclxuXHJcbiMjIEN1cnJlbnQgVW5yZXNvbHZlZCBRdWVzdGlvbnNcclxuXHJcbnt7dW5yZXNvbHZlZFF1ZXN0aW9uc319XHJcblxyXG4jIyBUYXNrXHJcblxyXG5BbmFseXplIHdoYXQgY2hhbmdlZCBpbiB0aGlzIGNoYXB0ZXIuIE91dHB1dCBKU09OIHdpdGg6XHJcblxyXG4xLiAqKmNoYXJhY3RlclVwZGF0ZXMqKjogSG93IGNoYXJhY3RlcnMgY2hhbmdlZCAoZW1vdGlvbiwgbG9jYXRpb24sIGtub3dsZWRnZSwgcmVsYXRpb25zaGlwcylcclxuMi4gKipwbG90VGhyZWFkVXBkYXRlcyoqOiBIb3cgcGxvdCB0aHJlYWRzIHByb2dyZXNzZWQgKHN0YXR1cywgdGVuc2lvbilcclxuMy4gKipuZXdRdWVzdGlvbnMqKjogTmV3IG15c3RlcmllcyBvciBxdWVzdGlvbnMgcmFpc2VkXHJcbjQuICoqcmVzb2x2ZWRRdWVzdGlvbnMqKjogV2hpY2ggY3VycmVudCBxdWVzdGlvbnMgd2VyZSBhbnN3ZXJlZFxyXG41LiAqKnJlY2VudEV2ZW50cyoqOiBLZXkgZXZlbnRzIHRoYXQgaGFwcGVuZWQgKDItMyBidWxsZXQgcG9pbnRzKVxyXG5cclxuRXhhbXBsZSBvdXRwdXQ6XHJcbntcclxuICBcImNoYXJhY3RlclVwZGF0ZXNcIjogW1xyXG4gICAge1xyXG4gICAgICBcIm5hbWVcIjogXCJBbGljZVwiLFxyXG4gICAgICBcImVtb3Rpb25hbFN0YXRlXCI6IFwiYW54aW91c1wiLFxyXG4gICAgICBcImxvY2F0aW9uXCI6IFwidGhlIGFiYW5kb25lZCB3YXJlaG91c2VcIixcclxuICAgICAgXCJuZXdLbm93bGVkZ2VcIjogW1widGhlIGNvZGUgaXMgODQ3MlwiXSxcclxuICAgICAgXCJyZWxhdGlvbnNoaXBDaGFuZ2VzXCI6IFt7XCJ3aXRoXCI6IFwiQm9iXCIsIFwic3RhdHVzXCI6IFwiZGlzdHJ1c3RmdWxcIn1dLFxyXG4gICAgICBcImRldmVsb3BtZW50XCI6IFwiQWxpY2UgcmVhbGl6ZXMgc2hlIGNhbid0IHRydXN0IGhlciBtZW50b3JcIlxyXG4gICAgfVxyXG4gIF0sXHJcbiAgXCJwbG90VGhyZWFkVXBkYXRlc1wiOiBbXHJcbiAgICB7XHJcbiAgICAgIFwiaWRcIjogXCJtaXNzaW5nX2Jyb3RoZXJcIixcclxuICAgICAgXCJzdGF0dXNcIjogXCJlc2NhbGF0aW5nXCIsXHJcbiAgICAgIFwidGVuc2lvbkNoYW5nZVwiOiAwLjEsXHJcbiAgICAgIFwic3VtbWFyeVwiOiBcIkRpc2NvdmVyZWQgYnJvdGhlciB3YXMga2lkbmFwcGVkIGJ5IHRoZSBzeW5kaWNhdGVcIlxyXG4gICAgfVxyXG4gIF0sXHJcbiAgXCJuZXdRdWVzdGlvbnNcIjogW1wiV2hvIGlzIHRoZSBteXN0ZXJpb3VzIGluZm9ybWFudD9cIl0sXHJcbiAgXCJyZXNvbHZlZFF1ZXN0aW9uc1wiOiBbXCJXaGVyZSB3YXMgdGhlIGJyb3RoZXIgdGFrZW4/XCJdLFxyXG4gIFwicmVjZW50RXZlbnRzXCI6IFtcIkFsaWNlIGJyb2tlIGludG8gdGhlIHdhcmVob3VzZVwiLCBcIkZvdW5kIHRoZSBoaWRkZW4gcm9vbVwiXVxyXG59YDtcclxuXHJcbmV4cG9ydCBjbGFzcyBTdGF0ZVVwZGF0ZXIge1xyXG4gIGFzeW5jIGV4dHJhY3RTdGF0ZUNoYW5nZXMoXHJcbiAgICBjaGFwdGVyOiBDaGFwdGVyLFxyXG4gICAgYmlibGU6IFN0b3J5QmlibGUsXHJcbiAgICBjdXJyZW50U3RhdGU6IFN0b3J5U3RydWN0dXJlZFN0YXRlXHJcbiAgKTogUHJvbWlzZTxTdGF0ZVVwZGF0ZU91dHB1dD4ge1xyXG4gICAgY29uc3QgY2hhcmFjdGVycyA9IE9iamVjdC52YWx1ZXMoY3VycmVudFN0YXRlLmNoYXJhY3RlcnMpXHJcbiAgICAgIC5tYXAoYyA9PiBgLSAke2MubmFtZX06IGN1cnJlbnRseSAke2MuZW1vdGlvbmFsU3RhdGV9LCBhdCAke2MubG9jYXRpb259YClcclxuICAgICAgLmpvaW4oJ1xcbicpIHx8ICdObyBjaGFyYWN0ZXJzIHRyYWNrZWQgeWV0Lic7XHJcblxyXG4gICAgY29uc3QgcGxvdFRocmVhZHMgPSBPYmplY3QudmFsdWVzKGN1cnJlbnRTdGF0ZS5wbG90VGhyZWFkcylcclxuICAgICAgLm1hcCh0ID0+IGAtICR7dC5uYW1lfSAoJHt0LnN0YXR1c30sIHRlbnNpb246ICR7TWF0aC5yb3VuZCh0LnRlbnNpb24gKiAxMDApfSUpOiAke3Quc3VtbWFyeX1gKVxyXG4gICAgICAuam9pbignXFxuJykgfHwgJ05vIGFjdGl2ZSBwbG90IHRocmVhZHMuJztcclxuXHJcbiAgICBjb25zdCB1bnJlc29sdmVkUXVlc3Rpb25zID0gY3VycmVudFN0YXRlLnVucmVzb2x2ZWRRdWVzdGlvbnMubGVuZ3RoID4gMFxyXG4gICAgICA/IGN1cnJlbnRTdGF0ZS51bnJlc29sdmVkUXVlc3Rpb25zLm1hcChxID0+IGAtICR7cX1gKS5qb2luKCdcXG4nKVxyXG4gICAgICA6ICdOb25lJztcclxuXHJcbiAgICBjb25zdCBwcm9tcHQgPSBTVEFURV9VUERBVEVfUFJPTVBUXHJcbiAgICAgIC5yZXBsYWNlKCd7e3RpdGxlfX0nLCBiaWJsZS50aXRsZSlcclxuICAgICAgLnJlcGxhY2UoJ3t7Z2VucmV9fScsIGJpYmxlLmdlbnJlKVxyXG4gICAgICAucmVwbGFjZSgne3tjaGFyYWN0ZXJzfX0nLCBjaGFyYWN0ZXJzKVxyXG4gICAgICAucmVwbGFjZSgne3twbG90VGhyZWFkc319JywgcGxvdFRocmVhZHMpXHJcbiAgICAgIC5yZXBsYWNlKCd7e2NoYXB0ZXJOdW1iZXJ9fScsIGNoYXB0ZXIubnVtYmVyLnRvU3RyaW5nKCkpXHJcbiAgICAgIC5yZXBsYWNlKCd7e2NoYXB0ZXJUaXRsZX19JywgY2hhcHRlci50aXRsZSlcclxuICAgICAgLnJlcGxhY2UoJ3t7Y2hhcHRlckNvbnRlbnR9fScsIGNoYXB0ZXIuY29udGVudC5zdWJzdHJpbmcoMCwgNjAwMCkpXHJcbiAgICAgIC5yZXBsYWNlKCd7e3VucmVzb2x2ZWRRdWVzdGlvbnN9fScsIHVucmVzb2x2ZWRRdWVzdGlvbnMpO1xyXG5cclxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGdldExMTSgpLmNvbXBsZXRlSlNPTjxTdGF0ZVVwZGF0ZU91dHB1dD4ocHJvbXB0LCB7XHJcbiAgICAgIHRlbXBlcmF0dXJlOiAwLjMsXHJcbiAgICAgIG1heFRva2VuczogMjAwMCxcclxuICAgIH0pO1xyXG5cclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfVxyXG5cclxuICBhcHBseVVwZGF0ZXMoXHJcbiAgICBzdGF0ZTogU3RvcnlTdHJ1Y3R1cmVkU3RhdGUsXHJcbiAgICB1cGRhdGVzOiBTdGF0ZVVwZGF0ZU91dHB1dCxcclxuICAgIGNoYXB0ZXJOdW1iZXI6IG51bWJlclxyXG4gICk6IFN0b3J5U3RydWN0dXJlZFN0YXRlIHtcclxuICAgIGxldCBuZXdTdGF0ZSA9IHsgLi4uc3RhdGUgfTtcclxuXHJcbiAgICAvLyBBcHBseSBjaGFyYWN0ZXIgdXBkYXRlc1xyXG4gICAgZm9yIChjb25zdCB1cGRhdGUgb2YgdXBkYXRlcy5jaGFyYWN0ZXJVcGRhdGVzKSB7XHJcbiAgICAgIGlmIChuZXdTdGF0ZS5jaGFyYWN0ZXJzW3VwZGF0ZS5uYW1lXSkge1xyXG4gICAgICAgIGNvbnN0IGNoYXIgPSBuZXdTdGF0ZS5jaGFyYWN0ZXJzW3VwZGF0ZS5uYW1lXTtcclxuICAgICAgICBcclxuICAgICAgICBpZiAodXBkYXRlLmVtb3Rpb25hbFN0YXRlKSB7XHJcbiAgICAgICAgICBjaGFyLmVtb3Rpb25hbFN0YXRlID0gdXBkYXRlLmVtb3Rpb25hbFN0YXRlO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAodXBkYXRlLmxvY2F0aW9uKSB7XHJcbiAgICAgICAgICBjaGFyLmxvY2F0aW9uID0gdXBkYXRlLmxvY2F0aW9uO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAodXBkYXRlLm5ld0tub3dsZWRnZSkge1xyXG4gICAgICAgICAgY2hhci5rbm93bGVkZ2UgPSBbLi4uY2hhci5rbm93bGVkZ2UsIC4uLnVwZGF0ZS5uZXdLbm93bGVkZ2VdO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAodXBkYXRlLnJlbGF0aW9uc2hpcENoYW5nZXMpIHtcclxuICAgICAgICAgIGZvciAoY29uc3QgcmVsIG9mIHVwZGF0ZS5yZWxhdGlvbnNoaXBDaGFuZ2VzKSB7XHJcbiAgICAgICAgICAgIGNoYXIucmVsYXRpb25zaGlwc1tyZWwud2l0aF0gPSByZWwuc3RhdHVzO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAodXBkYXRlLmRldmVsb3BtZW50KSB7XHJcbiAgICAgICAgICBjaGFyLmRldmVsb3BtZW50ID0gWy4uLmNoYXIuZGV2ZWxvcG1lbnQsIHVwZGF0ZS5kZXZlbG9wbWVudF07XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQXBwbHkgcGxvdCB0aHJlYWQgdXBkYXRlc1xyXG4gICAgZm9yIChjb25zdCB1cGRhdGUgb2YgdXBkYXRlcy5wbG90VGhyZWFkVXBkYXRlcykge1xyXG4gICAgICBpZiAobmV3U3RhdGUucGxvdFRocmVhZHNbdXBkYXRlLmlkXSkge1xyXG4gICAgICAgIGNvbnN0IHRocmVhZCA9IG5ld1N0YXRlLnBsb3RUaHJlYWRzW3VwZGF0ZS5pZF07XHJcbiAgICAgICAgXHJcbiAgICAgICAgaWYgKHVwZGF0ZS5zdGF0dXMpIHtcclxuICAgICAgICAgIHRocmVhZC5zdGF0dXMgPSB1cGRhdGUuc3RhdHVzO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAodXBkYXRlLnRlbnNpb25DaGFuZ2UgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgICAgdGhyZWFkLnRlbnNpb24gPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxLCB0aHJlYWQudGVuc2lvbiArIHVwZGF0ZS50ZW5zaW9uQ2hhbmdlKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmICh1cGRhdGUuc3VtbWFyeSkge1xyXG4gICAgICAgICAgdGhyZWFkLnN1bW1hcnkgPSB1cGRhdGUuc3VtbWFyeTtcclxuICAgICAgICB9XHJcbiAgICAgICAgdGhyZWFkLmxhc3RDaGFwdGVyID0gY2hhcHRlck51bWJlcjtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEFkZCBuZXcgcXVlc3Rpb25zXHJcbiAgICBmb3IgKGNvbnN0IHF1ZXN0aW9uIG9mIHVwZGF0ZXMubmV3UXVlc3Rpb25zKSB7XHJcbiAgICAgIGlmICghbmV3U3RhdGUudW5yZXNvbHZlZFF1ZXN0aW9ucy5pbmNsdWRlcyhxdWVzdGlvbikpIHtcclxuICAgICAgICBuZXdTdGF0ZS51bnJlc29sdmVkUXVlc3Rpb25zID0gWy4uLm5ld1N0YXRlLnVucmVzb2x2ZWRRdWVzdGlvbnMsIHF1ZXN0aW9uXTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbW92ZSByZXNvbHZlZCBxdWVzdGlvbnNcclxuICAgIGZvciAoY29uc3QgcXVlc3Rpb24gb2YgdXBkYXRlcy5yZXNvbHZlZFF1ZXN0aW9ucykge1xyXG4gICAgICBuZXdTdGF0ZS51bnJlc29sdmVkUXVlc3Rpb25zID0gbmV3U3RhdGUudW5yZXNvbHZlZFF1ZXN0aW9ucy5maWx0ZXIocSA9PiBxICE9PSBxdWVzdGlvbik7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQWRkIHJlY2VudCBldmVudHNcclxuICAgIGZvciAoY29uc3QgZXZlbnQgb2YgdXBkYXRlcy5yZWNlbnRFdmVudHMpIHtcclxuICAgICAgbmV3U3RhdGUucmVjZW50RXZlbnRzID0gWy4uLm5ld1N0YXRlLnJlY2VudEV2ZW50cywgZXZlbnRdLnNsaWNlKC0xMCk7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIG5ld1N0YXRlO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGNvbnN0IHN0YXRlVXBkYXRlciA9IG5ldyBTdGF0ZVVwZGF0ZXIoKTtcclxuIl19
@@ -0,0 +1,40 @@
1
+ import type { StoryBible, StoryState, ChapterSummary } from '../types/index.js';
2
+ import type { StoryStructuredState } from '../story/structuredState.js';
3
+ import type { TensionGuidance } from './tensionController.js';
4
+ export interface ChapterObjective {
5
+ id: string;
6
+ description: string;
7
+ priority: 'critical' | 'high' | 'medium' | 'low';
8
+ type: 'plot' | 'character' | 'world' | 'tension' | 'resolution';
9
+ relatedPlotThreadId?: string;
10
+ relatedCharacter?: string;
11
+ }
12
+ export interface DirectorOutput {
13
+ chapterNumber: number;
14
+ overallGoal: string;
15
+ objectives: ChapterObjective[];
16
+ focusCharacters: string[];
17
+ suggestedScenes: string[];
18
+ tone: string;
19
+ notes: string;
20
+ }
21
+ export interface DirectorContext {
22
+ bible: StoryBible;
23
+ state: StoryState;
24
+ structuredState: StoryStructuredState;
25
+ tensionGuidance: TensionGuidance;
26
+ previousSummaries: ChapterSummary[];
27
+ }
28
+ export declare class StoryDirector {
29
+ direct(context: DirectorContext): Promise<DirectorOutput>;
30
+ private buildPrompt;
31
+ /**
32
+ * Format director output for writer prompt
33
+ */
34
+ formatForPrompt(output: DirectorOutput): string;
35
+ /**
36
+ * Get quick objectives without LLM call (for testing/fallback)
37
+ */
38
+ generateFallbackObjectives(state: StoryState, structuredState: StoryStructuredState): DirectorOutput;
39
+ }
40
+ export declare const storyDirector: StoryDirector;
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.storyDirector = exports.StoryDirector = void 0;
4
+ const client_js_1 = require("../llm/client.js");
5
+ const STORY_DIRECTOR_PROMPT = `You are the Story Director for a narrative AI system. Your job is to decide what the next chapter should accomplish.
6
+
7
+ ## Story Bible
8
+
9
+ **Title:** {{title}}
10
+ **Genre:** {{genre}}
11
+ **Theme:** {{theme}}
12
+ **Premise:** {{premise}}
13
+
14
+ ## Current Story State
15
+
16
+ **Chapter:** {{currentChapter}} / {{totalChapters}}
17
+ **Story Tension:** {{storyTension}}%
18
+ **Target Tension:** {{targetTension}}%
19
+
20
+ ### Active Plot Threads
21
+ {{plotThreads}}
22
+
23
+ ### Character States
24
+ {{characters}}
25
+
26
+ ### Unresolved Questions
27
+ {{questions}}
28
+
29
+ ### Recent Events
30
+ {{recentEvents}}
31
+
32
+ ## Tension Guidance
33
+
34
+ {{tensionGuidance}}
35
+
36
+ ## Previous Chapter Summaries
37
+ {{summaries}}
38
+
39
+ ## Your Task
40
+
41
+ Based on all the above information, decide what Chapter {{nextChapter}} should accomplish. Consider:
42
+
43
+ 1. **Plot Progression**: Which plot threads need advancement?
44
+ 2. **Character Development**: Which characters need focus or growth?
45
+ 3. **Tension Management**: How should tension change based on target?
46
+ 4. **Unresolved Questions**: Which mysteries should be addressed?
47
+ 5. **Story Arc**: Where are we in the overall narrative (setup/rising action/climax/resolution)?
48
+
49
+ Output a JSON object with:
50
+
51
+ {
52
+ "chapterNumber": {{nextChapter}},
53
+ "overallGoal": "One-sentence description of what this chapter achieves",
54
+ "objectives": [
55
+ {
56
+ "id": "unique-id",
57
+ "description": "What needs to happen",
58
+ "priority": "critical|high|medium|low",
59
+ "type": "plot|character|world|tension|resolution",
60
+ "relatedPlotThreadId": "thread-id or omit",
61
+ "relatedCharacter": "character name or omit"
62
+ }
63
+ ],
64
+ "focusCharacters": ["Character names that should be central"],
65
+ "suggestedScenes": ["Scene ideas that could achieve objectives"],
66
+ "tone": "emotional tone for this chapter",
67
+ "notes": "Additional guidance for the writer"
68
+ }
69
+
70
+ Be specific and actionable. The writer will use your direction to craft the chapter.`;
71
+ class StoryDirector {
72
+ async direct(context) {
73
+ const { bible, state, structuredState, tensionGuidance, previousSummaries } = context;
74
+ const prompt = this.buildPrompt(bible, state, structuredState, tensionGuidance, previousSummaries);
75
+ const result = await (0, client_js_1.getLLM)().completeJSON(prompt, {
76
+ temperature: 0.4,
77
+ maxTokens: 2000,
78
+ });
79
+ return result;
80
+ }
81
+ buildPrompt(bible, state, structuredState, tensionGuidance, summaries) {
82
+ const currentTension = Math.round(structuredState.tension * 100);
83
+ const targetTension = Math.round(tensionGuidance.targetTension * 100);
84
+ // Format plot threads
85
+ const activeThreads = Object.values(structuredState.plotThreads)
86
+ .filter(t => t.status !== 'resolved')
87
+ .map(t => `- **${t.name}** (${t.status}, ${Math.round(t.tension * 100)}% tension): ${t.summary}`)
88
+ .join('\n') || 'No active plot threads.';
89
+ // Format characters
90
+ const characters = Object.values(structuredState.characters)
91
+ .map(c => `- **${c.name}**: ${c.emotionalState}, at ${c.location}`)
92
+ .join('\n') || 'No character data.';
93
+ // Format questions
94
+ const questions = structuredState.unresolvedQuestions.length > 0
95
+ ? structuredState.unresolvedQuestions.map(q => `- ${q}`).join('\n')
96
+ : 'None';
97
+ // Format recent events
98
+ const recentEvents = structuredState.recentEvents.length > 0
99
+ ? structuredState.recentEvents.slice(-5).map(e => `- ${e}`).join('\n')
100
+ : 'None';
101
+ // Format tension guidance
102
+ const tensionText = `Target: ${targetTension}%
103
+ Guidance: ${tensionGuidance.guidance}
104
+ Scene Types: ${tensionGuidance.sceneTypes.join(', ')}
105
+ Pacing: ${tensionGuidance.pacingNotes}`;
106
+ // Format summaries (last 3)
107
+ const recentSummaries = summaries.slice(-3);
108
+ const summariesText = recentSummaries.length > 0
109
+ ? recentSummaries.map(s => `Chapter ${s.chapterNumber}: ${s.summary}`).join('\n')
110
+ : 'No previous chapters.';
111
+ return STORY_DIRECTOR_PROMPT
112
+ .replace('{{title}}', bible.title)
113
+ .replace('{{genre}}', bible.genre)
114
+ .replace('{{theme}}', bible.theme)
115
+ .replace('{{premise}}', bible.premise)
116
+ .replace('{{currentChapter}}', state.currentChapter.toString())
117
+ .replace('{{totalChapters}}', state.totalChapters.toString())
118
+ .replace('{{storyTension}}', currentTension.toString())
119
+ .replace('{{targetTension}}', targetTension.toString())
120
+ .replace('{{plotThreads}}', activeThreads)
121
+ .replace('{{characters}}', characters)
122
+ .replace('{{questions}}', questions)
123
+ .replace('{{recentEvents}}', recentEvents)
124
+ .replace('{{tensionGuidance}}', tensionText)
125
+ .replace('{{summaries}}', summariesText)
126
+ .replace(/{{nextChapter}}/g, (state.currentChapter + 1).toString());
127
+ }
128
+ /**
129
+ * Format director output for writer prompt
130
+ */
131
+ formatForPrompt(output) {
132
+ const lines = ['## Chapter Direction'];
133
+ lines.push(`\n**Chapter ${output.chapterNumber} Goal:** ${output.overallGoal}`);
134
+ lines.push(`\n**Tone:** ${output.tone}`);
135
+ if (output.focusCharacters.length > 0) {
136
+ lines.push(`\n**Focus Characters:** ${output.focusCharacters.join(', ')}`);
137
+ }
138
+ if (output.objectives.length > 0) {
139
+ lines.push('\n**Objectives (in priority order):**');
140
+ const sorted = [...output.objectives].sort((a, b) => {
141
+ const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
142
+ return priorityOrder[a.priority] - priorityOrder[b.priority];
143
+ });
144
+ for (const obj of sorted) {
145
+ const emoji = { critical: '🔴', high: '🟠', medium: '🟡', low: '🟢' }[obj.priority];
146
+ lines.push(`${emoji} **[${obj.type.toUpperCase()}]** ${obj.description}`);
147
+ }
148
+ }
149
+ if (output.suggestedScenes.length > 0) {
150
+ lines.push('\n**Suggested Scenes:**');
151
+ for (const scene of output.suggestedScenes) {
152
+ lines.push(`- ${scene}`);
153
+ }
154
+ }
155
+ if (output.notes) {
156
+ lines.push(`\n**Director's Notes:** ${output.notes}`);
157
+ }
158
+ return lines.join('\n');
159
+ }
160
+ /**
161
+ * Get quick objectives without LLM call (for testing/fallback)
162
+ */
163
+ generateFallbackObjectives(state, structuredState) {
164
+ const nextChapter = state.currentChapter + 1;
165
+ const objectives = [];
166
+ // Add plot thread objectives
167
+ const activeThreads = Object.values(structuredState.plotThreads)
168
+ .filter(t => t.status === 'active' || t.status === 'escalating');
169
+ for (const thread of activeThreads.slice(0, 2)) {
170
+ objectives.push({
171
+ id: `plot-${thread.id}`,
172
+ description: `Advance the "${thread.name}" plot thread`,
173
+ priority: 'high',
174
+ type: 'plot',
175
+ relatedPlotThreadId: thread.id,
176
+ });
177
+ }
178
+ // Add character objective
179
+ const characters = Object.values(structuredState.characters);
180
+ if (characters.length > 0) {
181
+ const char = characters[nextChapter % characters.length];
182
+ objectives.push({
183
+ id: `char-${char.name}`,
184
+ description: `Develop ${char.name}'s character arc`,
185
+ priority: 'medium',
186
+ type: 'character',
187
+ relatedCharacter: char.name,
188
+ });
189
+ }
190
+ // Add tension objective if needed
191
+ const targetTension = 4 * (nextChapter / state.totalChapters) * (1 - nextChapter / state.totalChapters);
192
+ if (structuredState.tension < targetTension - 0.2) {
193
+ objectives.push({
194
+ id: 'tension-escalate',
195
+ description: 'Escalate tension toward target level',
196
+ priority: 'high',
197
+ type: 'tension',
198
+ });
199
+ }
200
+ return {
201
+ chapterNumber: nextChapter,
202
+ overallGoal: `Advance the story toward chapter ${nextChapter} with focus on active plot threads`,
203
+ objectives,
204
+ focusCharacters: characters.slice(0, 2).map(c => c.name),
205
+ suggestedScenes: ['Opening scene establishing current situation', 'Development of main plot thread', 'Character interaction moment'],
206
+ tone: 'dramatic',
207
+ notes: 'Auto-generated objectives based on current story state',
208
+ };
209
+ }
210
+ }
211
+ exports.StoryDirector = StoryDirector;
212
+ exports.storyDirector = new StoryDirector();
213
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcnlEaXJlY3Rvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hZ2VudHMvc3RvcnlEaXJlY3Rvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxnREFBMEM7QUFnQzFDLE1BQU0scUJBQXFCLEdBQUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O3FGQWlFdUQsQ0FBQztBQUV0RixNQUFhLGFBQWE7SUFDeEIsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUF3QjtRQUNuQyxNQUFNLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLGlCQUFpQixFQUFFLEdBQUcsT0FBTyxDQUFDO1FBRXRGLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLGlCQUFpQixDQUFDLENBQUM7UUFFbkcsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFBLGtCQUFNLEdBQUUsQ0FBQyxZQUFZLENBQWlCLE1BQU0sRUFBRTtZQUNqRSxXQUFXLEVBQUUsR0FBRztZQUNoQixTQUFTLEVBQUUsSUFBSTtTQUNoQixDQUFDLENBQUM7UUFFSCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRU8sV0FBVyxDQUNqQixLQUFpQixFQUNqQixLQUFpQixFQUNqQixlQUFxQyxFQUNyQyxlQUFnQyxFQUNoQyxTQUEyQjtRQUUzQixNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxPQUFPLEdBQUcsR0FBRyxDQUFDLENBQUM7UUFDakUsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxlQUFlLENBQUMsYUFBYSxHQUFHLEdBQUcsQ0FBQyxDQUFDO1FBRXRFLHNCQUFzQjtRQUN0QixNQUFNLGFBQWEsR0FBRyxNQUFNLENBQUMsTUFBTSxD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LGtCQUFrQjtnQkFDbkQsUUFBUSxFQUFFLFFBQVE7Z0JBQ2xCLElBQUksRUFBRSxXQUFXO2dCQUNqQixnQkFBZ0IsRUFBRSxJQUFJLENBQUMsSUFBSTthQUM1QixDQUFDLENBQUM7UUFDTCxDQUFDO1FBRUQsa0NBQWtDO1FBQ2xDLE1BQU0sYUFBYSxHQUFHLENBQUMsR0FBRyxDQUFDLFdBQVcsR0FBRyxLQUFLLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsV0FBVyxHQUFHLEtBQUssQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUN4RyxJQUFJLGVBQWUsQ0FBQyxPQUFPLEdBQUcsYUFBYSxHQUFHLEdBQUcsRUFBRSxDQUFDO1lBQ2xELFVBQVUsQ0FBQyxJQUFJLENBQUM7Z0JBQ2QsRUFBRSxFQUFFLGtCQUFrQjtnQkFDdEIsV0FBVyxFQUFFLHNDQUFzQztnQkFDbkQsUUFBUSxFQUFFLE1BQU07Z0JBQ2hCLElBQUksRUFBRSxTQUFTO2FBQ2hCLENBQUMsQ0FBQztRQUNMLENBQUM7UUFFRCxPQUFPO1lBQ0wsYUFBYSxFQUFFLFdBQVc7WUFDMUIsV0FBVyxFQUFFLG9DQUFvQyxXQUFXLG9DQUFvQztZQUNoRyxVQUFVO1lBQ1YsZUFBZSxFQUFFLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDeEQsZUFBZSxFQUFFLENBQUMsOENBQThDLEVBQUUsaUNBQWlDLEVBQUUsOEJBQThCLENBQUM7WUFDcEksSUFBSSxFQUFFLFVBQVU7WUFDaEIsS0FBSyxFQUFFLHdEQUF3RDtTQUNoRSxDQUFDO0lBQ0osQ0FBQztDQUNGO0FBN0tELHNDQTZLQztBQUVZLFFBQUEsYUFBYSxHQUFHLElBQUksYUFBYSxFQUFFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBnZXRMTE0gfSBmcm9tICcuLi9sbG0vY2xpZW50LmpzJztcclxuaW1wb3J0IHR5cGUgeyBTdG9yeUJpYmxlLCBTdG9yeVN0YXRlLCBDaGFwdGVyU3VtbWFyeSB9IGZyb20gJy4uL3R5cGVzL2luZGV4LmpzJztcclxuaW1wb3J0IHR5cGUgeyBTdG9yeVN0cnVjdHVyZWRTdGF0ZSB9IGZyb20gJy4uL3N0b3J5L3N0cnVjdHVyZWRTdGF0ZS5qcyc7XHJcbmltcG9ydCB0eXBlIHsgVGVuc2lvbkd1aWRhbmNlIH0gZnJvbSAnLi90ZW5zaW9uQ29udHJvbGxlci5qcyc7XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIENoYXB0ZXJPYmplY3RpdmUge1xyXG4gIGlkOiBzdHJpbmc7XHJcbiAgZGVzY3JpcHRpb246IHN0cmluZztcclxuICBwcmlvcml0eTogJ2NyaXRpY2FsJyB8ICdoaWdoJyB8ICdtZWRpdW0nIHwgJ2xvdyc7XHJcbiAgdHlwZTogJ3Bsb3QnIHwgJ2NoYXJhY3RlcicgfCAnd29ybGQnIHwgJ3RlbnNpb24nIHwgJ3Jlc29sdXRpb24nO1xyXG4gIHJlbGF0ZWRQbG90VGhyZWFkSWQ/OiBzdHJpbmc7XHJcbiAgcmVsYXRlZENoYXJhY3Rlcj86IHN0cmluZztcclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBEaXJlY3Rvck91dHB1dCB7XHJcbiAgY2hhcHRlck51bWJlcjogbnVtYmVyO1xyXG4gIG92ZXJhbGxHb2FsOiBzdHJpbmc7XHJcbiAgb2JqZWN0aXZlczogQ2hhcHRlck9iamVjdGl2ZVtdO1xyXG4gIGZvY3VzQ2hhcmFjdGVyczogc3RyaW5nW107XHJcbiAgc3VnZ2VzdGVkU2NlbmVzOiBzdHJpbmdbXTtcclxuICB0b25lOiBzdHJpbmc7XHJcbiAgbm90ZXM6IHN0cmluZztcclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBEaXJlY3RvckNvbnRleHQge1xyXG4gIGJpYmxlOiBTdG9yeUJpYmxlO1xyXG4gIHN0YXRlOiBTdG9yeVN0YXRlO1xyXG4gIHN0cnVjdHVyZWRTdGF0ZTogU3RvcnlTdHJ1Y3R1cmVkU3RhdGU7XHJcbiAgdGVuc2lvbkd1aWRhbmNlOiBUZW5zaW9uR3VpZGFuY2U7XHJcbiAgcHJldmlvdXNTdW1tYXJpZXM6IENoYXB0ZXJTdW1tYXJ5W107XHJcbn1cclxuXHJcbmNvbnN0IFNUT1JZX0RJUkVDVE9SX1BST01QVCA9IGBZb3UgYXJlIHRoZSBTdG9yeSBEaXJlY3RvciBmb3IgYSBuYXJyYXRpdmUgQUkgc3lzdGVtLiBZb3VyIGpvYiBpcyB0byBkZWNpZGUgd2hhdCB0aGUgbmV4dCBjaGFwdGVyIHNob3VsZCBhY2NvbXBsaXNoLlxyXG5cclxuIyMgU3RvcnkgQmlibGVcclxuXHJcbioqVGl0bGU6Kioge3t0aXRsZX19XHJcbioqR2VucmU6Kioge3tnZW5yZX19XHJcbioqVGhlbWU6Kioge3t0aGVtZX19XHJcbioqUHJlbWlzZToqKiB7e3ByZW1pc2V9fVxyXG5cclxuIyMgQ3VycmVudCBTdG9yeSBTdGF0ZVxyXG5cclxuKipDaGFwdGVyOioqIHt7Y3VycmVudENoYXB0ZXJ9fSAvIHt7dG90YWxDaGFwdGVyc319XHJcbioqU3RvcnkgVGVuc2lvbjoqKiB7e3N0b3J5VGVuc2lvbn19JVxyXG4qKlRhcmdldCBUZW5zaW9uOioqIHt7dGFyZ2V0VGVuc2lvbn19JVxyXG5cclxuIyMjIEFjdGl2ZSBQbG90IFRocmVhZHNcclxue3twbG90VGhyZWFkc319XHJcblxyXG4jIyMgQ2hhcmFjdGVyIFN0YXRlc1xyXG57e2NoYXJhY3RlcnN9fVxyXG5cclxuIyMjIFVucmVzb2x2ZWQgUXVlc3Rpb25zXHJcbnt7cXVlc3Rpb25zfX1cclxuXHJcbiMjIyBSZWNlbnQgRXZlbnRzXHJcbnt7cmVjZW50RXZlbnRzfX1cclxuXHJcbiMjIFRlbnNpb24gR3VpZGFuY2VcclxuXHJcbnt7dGVuc2lvbkd1aWRhbmNlfX1cclxuXHJcbiMjIFByZXZpb3VzIENoYXB0ZXIgU3VtbWFyaWVzXHJcbnt7c3VtbWFyaWVzfX1cclxuXHJcbiMjIFlvdXIgVGFza1xyXG5cclxuQmFzZWQgb24gYWxsIHRoZSBhYm92ZSBpbmZvcm1hdGlvbiwgZGVjaWRlIHdoYXQgQ2hhcHRlciB7e25leHRDaGFwdGVyfX0gc2hvdWxkIGFjY29tcGxpc2guIENvbnNpZGVyOlxyXG5cclxuMS4gKipQbG90IFByb2dyZXNzaW9uKio6IFdoaWNoIHBsb3QgdGhyZWFkcyBuZWVkIGFkdmFuY2VtZW50P1xyXG4yLiAqKkNoYXJhY3RlciBEZXZlbG9wbWVudCoqOiBXaGljaCBjaGFyYWN0ZXJzIG5lZWQgZm9jdXMgb3IgZ3Jvd3RoP1xyXG4zLiAqKlRlbnNpb24gTWFuYWdlbWVudCoqOiBIb3cgc2hvdWxkIHRlbnNpb24gY2hhbmdlIGJhc2VkIG9uIHRhcmdldD9cclxuNC4gKipVbnJlc29sdmVkIFF1ZXN0aW9ucyoqOiBXaGljaCBteXN0ZXJpZXMgc2hvdWxkIGJlIGFkZHJlc3NlZD9cclxuNS4gKipTdG9yeSBBcmMqKjogV2hlcmUgYXJlIHdlIGluIHRoZSBvdmVyYWxsIG5hcnJhdGl2ZSAoc2V0dXAvcmlzaW5nIGFjdGlvbi9jbGltYXgvcmVzb2x1dGlvbik/XHJcblxyXG5PdXRwdXQgYSBKU09OIG9iamVjdCB3aXRoOlxyXG5cclxue1xyXG4gIFwiY2hhcHRlck51bWJlclwiOiB7e25leHRDaGFwdGVyfX0sXHJcbiAgXCJvdmVyYWxsR29hbFwiOiBcIk9uZS1zZW50ZW5jZSBkZXNjcmlwdGlvbiBvZiB3aGF0IHRoaXMgY2hhcHRlciBhY2hpZXZlc1wiLFxyXG4gIFwib2JqZWN0aXZlc1wiOiBbXHJcbiAgICB7XHJcbiAgICAgIFwiaWRcIjogXCJ1bmlxdWUtaWRcIixcclxuICAgICAgXCJkZXNjcmlwdGlvblwiOiBcIldoYXQgbmVlZHMgdG8gaGFwcGVuXCIsXHJcbiAgICAgIFwicHJpb3JpdHlcIjogXCJjcml0aWNhbHxoaWdofG1lZGl1bXxsb3dcIixcclxuICAgICAgXCJ0eXBlXCI6IFwicGxvdHxjaGFyYWN0ZXJ8d29ybGR8dGVuc2lvbnxyZXNvbHV0aW9uXCIsXHJcbiAgICAgIFwicmVsYXRlZFBsb3RUaHJlYWRJZFwiOiBcInRocmVhZC1pZCBvciBvbWl0XCIsXHJcbiAgICAgIFwicmVsYXRlZENoYXJhY3RlclwiOiBcImNoYXJhY3RlciBuYW1lIG9yIG9taXRcIlxyXG4gICAgfVxyXG4gIF0sXHJcbiAgXCJmb2N1c0NoYXJhY3RlcnNcIjogW1wiQ2hhcmFjdGVyIG5hbWVzIHRoYXQgc2hvdWxkIGJlIGNlbnRyYWxcIl0sXHJcbiAgXCJzdWdnZXN0ZWRTY2VuZXNcIjogW1wiU2NlbmUgaWRlYXMgdGhhdCBjb3VsZCBhY2hpZXZlIG9iamVjdGl2ZXNcIl0sXHJcbiAgXCJ0b25lXCI6IFwiZW1vdGlvbmFsIHRvbmUgZm9yIHRoaXMgY2hhcHRlclwiLFxyXG4gIFwibm90ZXNcIjogXCJBZGRpdGlvbmFsIGd1aWRhbmNlIGZvciB0aGUgd3JpdGVyXCJcclxufVxyXG5cclxuQmUgc3BlY2lmaWMgYW5kIGFjdGlvbmFibGUuIFRoZSB3cml0ZXIgd2lsbCB1c2UgeW91ciBkaXJlY3Rpb24gdG8gY3JhZnQgdGhlIGNoYXB0ZXIuYDtcclxuXHJcbmV4cG9ydCBjbGFzcyBTdG9yeURpcmVjdG9yIHtcclxuICBhc3luYyBkaXJlY3QoY29udGV4dDogRGlyZWN0b3JDb250ZXh0KTogUHJvbWlzZTxEaXJlY3Rvck91dHB1dD4ge1xyXG4gICAgY29uc3QgeyBiaWJsZSwgc3RhdGUsIHN0cnVjdHVyZWRTdGF0ZSwgdGVuc2lvbkd1aWRhbmNlLCBwcmV2aW91c1N1bW1hcmllcyB9ID0gY29udGV4dDtcclxuICAgIFxyXG4gICAgY29uc3QgcHJvbXB0ID0gdGhpcy5idWlsZFByb21wdChiaWJsZSwgc3RhdGUsIHN0cnVjdHVyZWRTdGF0ZSwgdGVuc2lvbkd1aWRhbmNlLCBwcmV2aW91c1N1bW1hcmllcyk7XHJcbiAgICBcclxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGdldExMTSgpLmNvbXBsZXRlSlNPTjxEaXJlY3Rvck91dHB1dD4ocHJvbXB0LCB7XHJcbiAgICAgIHRlbXBlcmF0dXJlOiAwLjQsXHJcbiAgICAgIG1heFRva2VuczogMjAwMCxcclxuICAgIH0pO1xyXG4gICAgXHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG4gIH1cclxuICBcclxuICBwcml2YXRlIGJ1aWxkUHJvbXB0KFxyXG4gICAgYmlibGU6IFN0b3J5QmlibGUsXHJcbiAgICBzdGF0ZTogU3RvcnlTdGF0ZSxcclxuICAgIHN0cnVjdHVyZWRTdGF0ZTogU3RvcnlTdHJ1Y3R1cmVkU3RhdGUsXHJcbiAgICB0ZW5zaW9uR3VpZGFuY2U6IFRlbnNpb25HdWlkYW5jZSxcclxuICAgIHN1bW1hcmllczogQ2hhcHRlclN1bW1hcnlbXVxyXG4gICk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBjdXJyZW50VGVuc2lvbiA9IE1hdGgucm91bmQoc3RydWN0dXJlZFN0YXRlLnRlbnNpb24gKiAxMDApO1xyXG4gICAgY29uc3QgdGFyZ2V0VGVuc2lvbiA9IE1hdGgucm91bmQodGVuc2lvbkd1aWRhbmNlLnRhcmdldFRlbnNpb24gKiAxMDApO1xyXG4gICAgXHJcbiAgICAvLyBGb3JtYXQgcGxvdCB0aHJlYWRzXHJcbiAgICBjb25zdCBhY3RpdmVUaHJlYWRzID0gT2JqZWN0LnZhbHVlcyhzdHJ1Y3R1cmVkU3RhdGUucGxvdFRocmVhZHMpXHJcbiAgICAgIC5maWx0ZXIodCA9PiB0LnN0YXR1cyAhPT0gJ3Jlc29sdmVkJylcclxuICAgICAgLm1hcCh0ID0+IGAtICoqJHt0Lm5hbWV9KiogKCR7dC5zdGF0dXN9LCAke01hdGgucm91bmQodC50ZW5zaW9uICogMTAwKX0lIHRlbnNpb24pOiAke3Quc3VtbWFyeX1gKVxyXG4gICAgICAuam9pbignXFxuJykgfHwgJ05vIGFjdGl2ZSBwbG90IHRocmVhZHMuJztcclxuICAgIFxyXG4gICAgLy8gRm9ybWF0IGNoYXJhY3RlcnNcclxuICAgIGNvbnN0IGNoYXJhY3RlcnMgPSBPYmplY3QudmFsdWVzKHN0cnVjdHVyZWRTdGF0ZS5jaGFyYWN0ZXJzKVxyXG4gICAgICAubWFwKGMgPT4gYC0gKioke2MubmFtZX0qKjogJHtjLmVtb3Rpb25hbFN0YXRlfSwgYXQgJHtjLmxvY2F0aW9ufWApXHJcbiAgICAgIC5qb2luKCdcXG4nKSB8fCAnTm8gY2hhcmFjdGVyIGRhdGEuJztcclxuICAgIFxyXG4gICAgLy8gRm9ybWF0IHF1ZXN0aW9uc1xyXG4gICAgY29uc3QgcXVlc3Rpb25zID0gc3RydWN0dXJlZFN0YXRlLnVucmVzb2x2ZWRRdWVzdGlvbnMubGVuZ3RoID4gMFxyXG4gICAgICA/IHN0cnVjdHVyZWRTdGF0ZS51bnJlc29sdmVkUXVlc3Rpb25zLm1hcChxID0+IGAtICR7cX1gKS5qb2luKCdcXG4nKVxyXG4gICAgICA6ICdOb25lJztcclxuICAgIFxyXG4gICAgLy8gRm9ybWF0IHJlY2VudCBldmVudHNcclxuICAgIGNvbnN0IHJlY2VudEV2ZW50cyA9IHN0cnVjdHVyZWRTdGF0ZS5yZWNlbnRFdmVudHMubGVuZ3RoID4gMFxyXG4gICAgICA/IHN0cnVjdHVyZWRTdGF0ZS5yZWNlbnRFdmVudHMuc2xpY2UoLTUpLm1hcChlID0+IGAtICR7ZX1gKS5qb2luKCdcXG4nKVxyXG4gICAgICA6ICdOb25lJztcclxuICAgIFxyXG4gICAgLy8gRm9ybWF0IHRlbnNpb24gZ3VpZGFuY2VcclxuICAgIGNvbnN0IHRlbnNpb25UZXh0ID0gYFRhcmdldDogJHt0YXJnZXRUZW5zaW9ufSVcclxuR3VpZGFuY2U6ICR7dGVuc2lvbkd1aWRhbmNlLmd1aWRhbmNlfVxyXG5TY2VuZSBUeXBlczogJHt0ZW5zaW9uR3VpZGFuY2Uuc2NlbmVUeXBlcy5qb2luKCcsICcpfVxyXG5QYWNpbmc6ICR7dGVuc2lvbkd1aWRhbmNlLnBhY2luZ05vdGVzfWA7XHJcbiAgICBcclxuICAgIC8vIEZvcm1hdCBzdW1tYXJpZXMgKGxhc3QgMylcclxuICAgIGNvbnN0IHJlY2VudFN1bW1hcmllcyA9IHN1bW1hcmllcy5zbGljZSgtMyk7XHJcbiAgICBjb25zdCBzdW1tYXJpZXNUZXh0ID0gcmVjZW50U3VtbWFyaWVzLmxlbmd0aCA+IDBcclxuICAgICAgPyByZWNlbnRTdW1tYXJpZXMubWFwKHMgPT4gYENoYXB0ZXIgJHtzLmNoYXB0ZXJOdW1iZXJ9OiAke3Muc3VtbWFyeX1gKS5qb2luKCdcXG4nKVxyXG4gICAgICA6ICdObyBwcmV2aW91cyBjaGFwdGVycy4nO1xyXG4gICAgXHJcbiAgICByZXR1cm4gU1RPUllfRElSRUNUT1JfUFJPTVBUXHJcbiAgICAgIC5yZXBsYWNlKCd7e3RpdGxlfX0nLCBiaWJsZS50aXRsZSlcclxuICAgICAgLnJlcGxhY2UoJ3t7Z2VucmV9fScsIGJpYmxlLmdlbnJlKVxyXG4gICAgICAucmVwbGFjZSgne3t0aGVtZX19JywgYmlibGUudGhlbWUpXHJcbiAgICAgIC5yZXBsYWNlKCd7e3ByZW1pc2V9fScsIGJpYmxlLnByZW1pc2UpXHJcbiAgICAgIC5yZXBsYWNlKCd7e2N1cnJlbnRDaGFwdGVyfX0nLCBzdGF0ZS5jdXJyZW50Q2hhcHRlci50b1N0cmluZygpKVxyXG4gICAgICAucmVwbGFjZSgne3t0b3RhbENoYXB0ZXJzfX0nLCBzdGF0ZS50b3RhbENoYXB0ZXJzLnRvU3RyaW5nKCkpXHJcbiAgICAgIC5yZXBsYWNlKCd7e3N0b3J5VGVuc2lvbn19JywgY3VycmVudFRlbnNpb24udG9TdHJpbmcoKSlcclxuICAgICAgLnJlcGxhY2UoJ3t7dGFyZ2V0VGVuc2lvbn19JywgdGFyZ2V0VGVuc2lvbi50b1N0cmluZygpKVxyXG4gICAgICAucmVwbGFjZSgne3twbG90VGhyZWFkc319JywgYWN0aXZlVGhyZWFkcylcclxuICAgICAgLnJlcGxhY2UoJ3t7Y2hhcmFjdGVyc319JywgY2hhcmFjdGVycylcclxuICAgICAgLnJlcGxhY2UoJ3t7cXVlc3Rpb25zfX0nLCBxdWVzdGlvbnMpXHJcbiAgICAgIC5yZXBsYWNlKCd7e3JlY2VudEV2ZW50c319JywgcmVjZW50RXZlbnRzKVxyXG4gICAgICAucmVwbGFjZSgne3t0ZW5zaW9uR3VpZGFuY2V9fScsIHRlbnNpb25UZXh0KVxyXG4gICAgICAucmVwbGFjZSgne3tzdW1tYXJpZXN9fScsIHN1bW1hcmllc1RleHQpXHJcbiAgICAgIC5yZXBsYWNlKC97e25leHRDaGFwdGVyfX0vZywgKHN0YXRlLmN1cnJlbnRDaGFwdGVyICsgMSkudG9TdHJpbmcoKSk7XHJcbiAgfVxyXG4gIFxyXG4gIC8qKlxyXG4gICAqIEZvcm1hdCBkaXJlY3RvciBvdXRwdXQgZm9yIHdyaXRlciBwcm9tcHRcclxuICAgKi9cclxuICBmb3JtYXRGb3JQcm9tcHQob3V0cHV0OiBEaXJlY3Rvck91dHB1dCk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBsaW5lczogc3RyaW5nW10gPSBbJyMjIENoYXB0ZXIgRGlyZWN0aW9uJ107XHJcbiAgICBcclxuICAgIGxpbmVzLnB1c2goYFxcbioqQ2hhcHRlciAke291dHB1dC5jaGFwdGVyTnVtYmVyfSBHb2FsOioqICR7b3V0cHV0Lm92ZXJhbGxHb2FsfWApO1xyXG4gICAgbGluZXMucHVzaChgXFxuKipUb25lOioqICR7b3V0cHV0LnRvbmV9YCk7XHJcbiAgICBcclxuICAgIGlmIChvdXRwdXQuZm9jdXNDaGFyYWN0ZXJzLmxlbmd0aCA+IDApIHtcclxuICAgICAgbGluZXMucHVzaChgXFxuKipGb2N1cyBDaGFyYWN0ZXJzOioqICR7b3V0cHV0LmZvY3VzQ2hhcmFjdGVycy5qb2luKCcsICcpfWApO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICBpZiAob3V0cHV0Lm9iamVjdGl2ZXMubGVuZ3RoID4gMCkge1xyXG4gICAgICBsaW5lcy5wdXNoKCdcXG4qKk9iamVjdGl2ZXMgKGluIHByaW9yaXR5IG9yZGVyKToqKicpO1xyXG4gICAgICBjb25zdCBzb3J0ZWQgPSBbLi4ub3V0cHV0Lm9iamVjdGl2ZXNdLnNvcnQoKGEsIGIpID0+IHtcclxuICAgICAgICBjb25zdCBwcmlvcml0eU9yZGVyID0geyBjcml0aWNhbDogMCwgaGlnaDogMSwgbWVkaXVtOiAyLCBsb3c6IDMgfTtcclxuICAgICAgICByZXR1cm4gcHJpb3JpdHlPcmRlclthLnByaW9yaXR5XSAtIHByaW9yaXR5T3JkZXJbYi5wcmlvcml0eV07XHJcbiAgICAgIH0pO1xyXG4gICAgICBcclxuICAgICAgZm9yIChjb25zdCBvYmogb2Ygc29ydGVkKSB7XHJcbiAgICAgICAgY29uc3QgZW1vamkgPSB7IGNyaXRpY2FsOiAn8J+UtCcsIGhpZ2g6ICfwn5+gJywgbWVkaXVtOiAn8J+foScsIGxvdzogJ/Cfn6InIH1bb2JqLnByaW9yaXR5XTtcclxuICAgICAgICBsaW5lcy5wdXNoKGAke2Vtb2ppfSAqKlske29iai50eXBlLnRvVXBwZXJDYXNlKCl9XSoqICR7b2JqLmRlc2NyaXB0aW9ufWApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBcclxuICAgIGlmIChvdXRwdXQuc3VnZ2VzdGVkU2NlbmVzLmxlbmd0aCA+IDApIHtcclxuICAgICAgbGluZXMucHVzaCgnXFxuKipTdWdnZXN0ZWQgU2NlbmVzOioqJyk7XHJcbiAgICAgIGZvciAoY29uc3Qgc2NlbmUgb2Ygb3V0cHV0LnN1Z2dlc3RlZFNjZW5lcykge1xyXG4gICAgICAgIGxpbmVzLnB1c2goYC0gJHtzY2VuZX1gKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgXHJcbiAgICBpZiAob3V0cHV0Lm5vdGVzKSB7XHJcbiAgICAgIGxpbmVzLnB1c2goYFxcbioqRGlyZWN0b3IncyBOb3RlczoqKiAke291dHB1dC5ub3Rlc31gKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgcmV0dXJuIGxpbmVzLmpvaW4oJ1xcbicpO1xyXG4gIH1cclxuICBcclxuICAvKipcclxuICAgKiBHZXQgcXVpY2sgb2JqZWN0aXZlcyB3aXRob3V0IExMTSBjYWxsIChmb3IgdGVzdGluZy9mYWxsYmFjaylcclxuICAgKi9cclxuICBnZW5lcmF0ZUZhbGxiYWNrT2JqZWN0aXZlcyhcclxuICAgIHN0YXRlOiBTdG9yeVN0YXRlLFxyXG4gICAgc3RydWN0dXJlZFN0YXRlOiBTdG9yeVN0cnVjdHVyZWRTdGF0ZVxyXG4gICk6IERpcmVjdG9yT3V0cHV0IHtcclxuICAgIGNvbnN0IG5leHRDaGFwdGVyID0gc3RhdGUuY3VycmVudENoYXB0ZXIgKyAxO1xyXG4gICAgY29uc3Qgb2JqZWN0aXZlczogQ2hhcHRlck9iamVjdGl2ZVtdID0gW107XHJcbiAgICBcclxuICAgIC8vIEFkZCBwbG90IHRocmVhZCBvYmplY3RpdmVzXHJcbiAgICBjb25zdCBhY3RpdmVUaHJlYWRzID0gT2JqZWN0LnZhbHVlcyhzdHJ1Y3R1cmVkU3RhdGUucGxvdFRocmVhZHMpXHJcbiAgICAgIC5maWx0ZXIodCA9PiB0LnN0YXR1cyA9PT0gJ2FjdGl2ZScgfHwgdC5zdGF0dXMgPT09ICdlc2NhbGF0aW5nJyk7XHJcbiAgICBcclxuICAgIGZvciAoY29uc3QgdGhyZWFkIG9mIGFjdGl2ZVRocmVhZHMuc2xpY2UoMCwgMikpIHtcclxuICAgICAgb2JqZWN0aXZlcy5wdXNoKHtcclxuICAgICAgICBpZDogYHBsb3QtJHt0aHJlYWQuaWR9YCxcclxuICAgICAgICBkZXNjcmlwdGlvbjogYEFkdmFuY2UgdGhlIFwiJHt0aHJlYWQubmFtZX1cIiBwbG90IHRocmVhZGAsXHJcbiAgICAgICAgcHJpb3JpdHk6ICdoaWdoJyxcclxuICAgICAgICB0eXBlOiAncGxvdCcsXHJcbiAgICAgICAgcmVsYXRlZFBsb3RUaHJlYWRJZDogdGhyZWFkLmlkLFxyXG4gICAgICB9KTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgLy8gQWRkIGNoYXJhY3RlciBvYmplY3RpdmVcclxuICAgIGNvbnN0IGNoYXJhY3RlcnMgPSBPYmplY3QudmFsdWVzKHN0cnVjdHVyZWRTdGF0ZS5jaGFyYWN0ZXJzKTtcclxuICAgIGlmIChjaGFyYWN0ZXJzLmxlbmd0aCA+IDApIHtcclxuICAgICAgY29uc3QgY2hhciA9IGNoYXJhY3RlcnNbbmV4dENoYXB0ZXIgJSBjaGFyYWN0ZXJzLmxlbmd0aF07XHJcbiAgICAgIG9iamVjdGl2ZXMucHVzaCh7XHJcbiAgICAgICAgaWQ6IGBjaGFyLSR7Y2hhci5uYW1lfWAsXHJcbiAgICAgICAgZGVzY3JpcHRpb246IGBEZXZlbG9wICR7Y2hhci5uYW1lfSdzIGNoYXJhY3RlciBhcmNgLFxyXG4gICAgICAgIHByaW9yaXR5OiAnbWVkaXVtJyxcclxuICAgICAgICB0eXBlOiAnY2hhcmFjdGVyJyxcclxuICAgICAgICByZWxhdGVkQ2hhcmFjdGVyOiBjaGFyLm5hbWUsXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICAvLyBBZGQgdGVuc2lvbiBvYmplY3RpdmUgaWYgbmVlZGVkXHJcbiAgICBjb25zdCB0YXJnZXRUZW5zaW9uID0gNCAqIChuZXh0Q2hhcHRlciAvIHN0YXRlLnRvdGFsQ2hhcHRlcnMpICogKDEgLSBuZXh0Q2hhcHRlciAvIHN0YXRlLnRvdGFsQ2hhcHRlcnMpO1xyXG4gICAgaWYgKHN0cnVjdHVyZWRTdGF0ZS50ZW5zaW9uIDwgdGFyZ2V0VGVuc2lvbiAtIDAuMikge1xyXG4gICAgICBvYmplY3RpdmVzLnB1c2goe1xyXG4gICAgICAgIGlkOiAndGVuc2lvbi1lc2NhbGF0ZScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFc2NhbGF0ZSB0ZW5zaW9uIHRvd2FyZCB0YXJnZXQgbGV2ZWwnLFxyXG4gICAgICAgIHByaW9yaXR5OiAnaGlnaCcsXHJcbiAgICAgICAgdHlwZTogJ3RlbnNpb24nLFxyXG4gICAgICB9KTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgY2hhcHRlck51bWJlcjogbmV4dENoYXB0ZXIsXHJcbiAgICAgIG92ZXJhbGxHb2FsOiBgQWR2YW5jZSB0aGUgc3RvcnkgdG93YXJkIGNoYXB0ZXIgJHtuZXh0Q2hhcHRlcn0gd2l0aCBmb2N1cyBvbiBhY3RpdmUgcGxvdCB0aHJlYWRzYCxcclxuICAgICAgb2JqZWN0aXZlcyxcclxuICAgICAgZm9jdXNDaGFyYWN0ZXJzOiBjaGFyYWN0ZXJzLnNsaWNlKDAsIDIpLm1hcChjID0+IGMubmFtZSksXHJcbiAgICAgIHN1Z2dlc3RlZFNjZW5lczogWydPcGVuaW5nIHNjZW5lIGVzdGFibGlzaGluZyBjdXJyZW50IHNpdHVhdGlvbicsICdEZXZlbG9wbWVudCBvZiBtYWluIHBsb3QgdGhyZWFkJywgJ0NoYXJhY3RlciBpbnRlcmFjdGlvbiBtb21lbnQnXSxcclxuICAgICAgdG9uZTogJ2RyYW1hdGljJyxcclxuICAgICAgbm90ZXM6ICdBdXRvLWdlbmVyYXRlZCBvYmplY3RpdmVzIGJhc2VkIG9uIGN1cnJlbnQgc3Rvcnkgc3RhdGUnLFxyXG4gICAgfTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBjb25zdCBzdG9yeURpcmVjdG9yID0gbmV3IFN0b3J5RGlyZWN0b3IoKTtcclxuIl19
@@ -0,0 +1,8 @@
1
+ import type { ChapterSummary } from '../types/index.js';
2
+ export declare class ChapterSummarizer {
3
+ private promptTemplate;
4
+ constructor();
5
+ summarize(chapterText: string, chapterNumber: number): Promise<ChapterSummary>;
6
+ private extractKeyEvents;
7
+ }
8
+ export declare const summarizer: ChapterSummarizer;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.summarizer = exports.ChapterSummarizer = void 0;
4
+ const client_js_1 = require("../llm/client.js");
5
+ const SUMMARIZER_PROMPT = `Summarize the following chapter in under 120 tokens.
6
+
7
+ Focus on:
8
+ - Major events that occurred
9
+ - Plot progress made
10
+ - Important character changes or revelations
11
+
12
+ ## Chapter Text
13
+
14
+ {{chapterText}}
15
+
16
+ ## Summary`;
17
+ class ChapterSummarizer {
18
+ promptTemplate;
19
+ constructor() {
20
+ this.promptTemplate = SUMMARIZER_PROMPT;
21
+ }
22
+ async summarize(chapterText, chapterNumber) {
23
+ const prompt = this.promptTemplate.replace('{{chapterText}}', chapterText);
24
+ const summary = await (0, client_js_1.getLLM)().complete(prompt, {
25
+ temperature: 0.3,
26
+ maxTokens: 200,
27
+ });
28
+ return {
29
+ chapterNumber,
30
+ summary: summary.trim(),
31
+ keyEvents: this.extractKeyEvents(chapterText),
32
+ characterChanges: {},
33
+ };
34
+ }
35
+ extractKeyEvents(text) {
36
+ const sentences = text.match(/[^.!?]+[.!?]+/g) || [];
37
+ const events = [];
38
+ for (const sentence of sentences.slice(0, 20)) {
39
+ const lower = sentence.toLowerCase();
40
+ if (lower.includes('discovered') ||
41
+ lower.includes('realized') ||
42
+ lower.includes('decided') ||
43
+ lower.includes('arrived') ||
44
+ lower.includes('found') ||
45
+ lower.includes('learned')) {
46
+ events.push(sentence.trim());
47
+ }
48
+ if (events.length >= 3)
49
+ break;
50
+ }
51
+ return events;
52
+ }
53
+ }
54
+ exports.ChapterSummarizer = ChapterSummarizer;
55
+ exports.summarizer = new ChapterSummarizer();
56
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3VtbWFyaXplci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hZ2VudHMvc3VtbWFyaXplci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxnREFBMEM7QUFHMUMsTUFBTSxpQkFBaUIsR0FBRzs7Ozs7Ozs7Ozs7V0FXZixDQUFDO0FBRVosTUFBYSxpQkFBaUI7SUFDcEIsY0FBYyxDQUFTO0lBRS9CO1FBQ0UsSUFBSSxDQUFDLGNBQWMsR0FBRyxpQkFBaUIsQ0FBQztJQUMxQyxDQUFDO0lBRUQsS0FBSyxDQUFDLFNBQVMsQ0FBQyxXQUFtQixFQUFFLGFBQXFCO1FBQ3hELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLGlCQUFpQixFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBRTNFLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBQSxrQkFBTSxHQUFFLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRTtZQUM5QyxXQUFXLEVBQUUsR0FBRztZQUNoQixTQUFTLEVBQUUsR0FBRztTQUNmLENBQUMsQ0FBQztRQUVILE9BQU87WUFDTCxhQUFhO1lBQ2IsT0FBTyxFQUFFLE9BQU8sQ0FBQyxJQUFJLEVBQUU7WUFDdkIsU0FBUyxFQUFFLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxXQUFXLENBQUM7WUFDN0MsZ0JBQWdCLEVBQUUsRUFBRTtTQUNyQixDQUFDO0lBQ0osQ0FBQztJQUVPLGdCQUFnQixDQUFDLElBQVk7UUFDbkMsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNyRCxNQUFNLE1BQU0sR0FBYSxFQUFFLENBQUM7UUFFNUIsS0FBSyxNQUFNLFFBQVEsSUFBSSxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQzlDLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNyQyxJQUNFLEtBQUssQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDO2dCQUM1QixLQUFLLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQztnQkFDMUIsS0FBSyxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUM7Z0JBQ3pCLEtBQUssQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDO2dCQUN6QixLQUFLLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDdkIsS0FBSyxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsRUFDekIsQ0FBQztnQkFDRCxNQUFNLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQy9CLENBQUM7WUFDRCxJQUFJLE1BQU0sQ0FBQyxNQUFNLElBQUksQ0FBQztnQkFBRSxNQUFNO1FBQ2hDLENBQUM7UUFFRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0NBQ0Y7QUE1Q0QsOENBNENDO0FBRVksUUFBQSxVQUFVLEdBQUcsSUFBSSxpQkFBaUIsRUFBRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZ2V0TExNIH0gZnJvbSAnLi4vbGxtL2NsaWVudC5qcyc7XHJcbmltcG9ydCB0eXBlIHsgQ2hhcHRlclN1bW1hcnkgfSBmcm9tICcuLi90eXBlcy9pbmRleC5qcyc7XHJcblxyXG5jb25zdCBTVU1NQVJJWkVSX1BST01QVCA9IGBTdW1tYXJpemUgdGhlIGZvbGxvd2luZyBjaGFwdGVyIGluIHVuZGVyIDEyMCB0b2tlbnMuXHJcblxyXG5Gb2N1cyBvbjpcclxuLSBNYWpvciBldmVudHMgdGhhdCBvY2N1cnJlZFxyXG4tIFBsb3QgcHJvZ3Jlc3MgbWFkZVxyXG4tIEltcG9ydGFudCBjaGFyYWN0ZXIgY2hhbmdlcyBvciByZXZlbGF0aW9uc1xyXG5cclxuIyMgQ2hhcHRlciBUZXh0XHJcblxyXG57e2NoYXB0ZXJUZXh0fX1cclxuXHJcbiMjIFN1bW1hcnlgO1xyXG5cclxuZXhwb3J0IGNsYXNzIENoYXB0ZXJTdW1tYXJpemVyIHtcclxuICBwcml2YXRlIHByb21wdFRlbXBsYXRlOiBzdHJpbmc7XHJcblxyXG4gIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgdGhpcy5wcm9tcHRUZW1wbGF0ZSA9IFNVTU1BUklaRVJfUFJPTVBUO1xyXG4gIH1cclxuXHJcbiAgYXN5bmMgc3VtbWFyaXplKGNoYXB0ZXJUZXh0OiBzdHJpbmcsIGNoYXB0ZXJOdW1iZXI6IG51bWJlcik6IFByb21pc2U8Q2hhcHRlclN1bW1hcnk+IHtcclxuICAgIGNvbnN0IHByb21wdCA9IHRoaXMucHJvbXB0VGVtcGxhdGUucmVwbGFjZSgne3tjaGFwdGVyVGV4dH19JywgY2hhcHRlclRleHQpO1xyXG5cclxuICAgIGNvbnN0IHN1bW1hcnkgPSBhd2FpdCBnZXRMTE0oKS5jb21wbGV0ZShwcm9tcHQsIHtcclxuICAgICAgdGVtcGVyYXR1cmU6IDAuMyxcclxuICAgICAgbWF4VG9rZW5zOiAyMDAsXHJcbiAgICB9KTtcclxuXHJcbiAgICByZXR1cm4ge1xyXG4gICAgICBjaGFwdGVyTnVtYmVyLFxyXG4gICAgICBzdW1tYXJ5OiBzdW1tYXJ5LnRyaW0oKSxcclxuICAgICAga2V5RXZlbnRzOiB0aGlzLmV4dHJhY3RLZXlFdmVudHMoY2hhcHRlclRleHQpLFxyXG4gICAgICBjaGFyYWN0ZXJDaGFuZ2VzOiB7fSxcclxuICAgIH07XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGV4dHJhY3RLZXlFdmVudHModGV4dDogc3RyaW5nKTogc3RyaW5nW10ge1xyXG4gICAgY29uc3Qgc2VudGVuY2VzID0gdGV4dC5tYXRjaCgvW14uIT9dK1suIT9dKy9nKSB8fCBbXTtcclxuICAgIGNvbnN0IGV2ZW50czogc3RyaW5nW10gPSBbXTtcclxuICAgIFxyXG4gICAgZm9yIChjb25zdCBzZW50ZW5jZSBvZiBzZW50ZW5jZXMuc2xpY2UoMCwgMjApKSB7XHJcbiAgICAgIGNvbnN0IGxvd2VyID0gc2VudGVuY2UudG9Mb3dlckNhc2UoKTtcclxuICAgICAgaWYgKFxyXG4gICAgICAgIGxvd2VyLmluY2x1ZGVzKCdkaXNjb3ZlcmVkJykgfHxcclxuICAgICAgICBsb3dlci5pbmNsdWRlcygncmVhbGl6ZWQnKSB8fFxyXG4gICAgICAgIGxvd2VyLmluY2x1ZGVzKCdkZWNpZGVkJykgfHxcclxuICAgICAgICBsb3dlci5pbmNsdWRlcygnYXJyaXZlZCcpIHx8XHJcbiAgICAgICAgbG93ZXIuaW5jbHVkZXMoJ2ZvdW5kJykgfHxcclxuICAgICAgICBsb3dlci5pbmNsdWRlcygnbGVhcm5lZCcpXHJcbiAgICAgICkge1xyXG4gICAgICAgIGV2ZW50cy5wdXNoKHNlbnRlbmNlLnRyaW0oKSk7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKGV2ZW50cy5sZW5ndGggPj0gMykgYnJlYWs7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIHJldHVybiBldmVudHM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgY29uc3Qgc3VtbWFyaXplciA9IG5ldyBDaGFwdGVyU3VtbWFyaXplcigpO1xyXG4iXX0=
@@ -0,0 +1,68 @@
1
+ import type { Chapter, StoryState } from '../types/index.js';
2
+ import type { StoryStructuredState } from '../story/structuredState.js';
3
+ export interface TensionAnalysis {
4
+ currentTension: number;
5
+ targetTension: number;
6
+ tensionGap: number;
7
+ recommendedAction: 'escalate' | 'maintain' | 'resolve' | 'climax';
8
+ reasoning: string;
9
+ }
10
+ export interface TensionGuidance {
11
+ targetTension: number;
12
+ guidance: string;
13
+ sceneTypes: string[];
14
+ pacingNotes: string;
15
+ }
16
+ /**
17
+ * Calculate target tension using parabolic curve
18
+ * Formula: targetTension = 4 * progress * (1 - progress)
19
+ *
20
+ * This creates a natural dramatic arc:
21
+ * - Chapter 0: 0% tension (setup)
22
+ * - Middle chapters: ~100% tension (peak drama)
23
+ * - Final chapter: 0% tension (resolution)
24
+ */
25
+ export declare function calculateTargetTension(currentChapter: number, totalChapters: number): number;
26
+ /**
27
+ * Calculate tension for next chapter based on ideal arc
28
+ */
29
+ export declare function calculateNextChapterTension(currentChapter: number, totalChapters: number): number;
30
+ /**
31
+ * Analyze current tension vs target and provide guidance
32
+ */
33
+ export declare function analyzeTension(storyState: StoryState, structuredState: StoryStructuredState): TensionAnalysis;
34
+ /**
35
+ * Generate tension guidance for the writer
36
+ */
37
+ export declare function generateTensionGuidance(analysis: TensionAnalysis, storyState: StoryState): TensionGuidance;
38
+ /**
39
+ * Format tension guidance for writer prompt
40
+ */
41
+ export declare function formatTensionForPrompt(guidance: TensionGuidance): string;
42
+ /**
43
+ * Calculate tension based on chapter content analysis
44
+ * This is a heuristic for estimating tension from text
45
+ */
46
+ export declare function estimateTensionFromChapter(chapter: Chapter): number;
47
+ /**
48
+ * TensionController class for managing story tension
49
+ */
50
+ export declare class TensionController {
51
+ /**
52
+ * Analyze current story state and provide tension guidance
53
+ */
54
+ analyze(storyState: StoryState, structuredState: StoryStructuredState): TensionAnalysis;
55
+ /**
56
+ * Generate guidance for the next chapter
57
+ */
58
+ generateGuidance(storyState: StoryState, structuredState: StoryStructuredState): TensionGuidance;
59
+ /**
60
+ * Calculate what the tension should be for a specific chapter
61
+ */
62
+ calculateTarget(chapterNumber: number, totalChapters: number): number;
63
+ /**
64
+ * Estimate tension from chapter content
65
+ */
66
+ estimateFromContent(chapter: Chapter): number;
67
+ }
68
+ export declare const tensionController: TensionController;