briyah 1.1.4 → 1.1.6

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 (89) hide show
  1. package/README.md +14 -1
  2. package/data/common/config/image_models.json +141 -21
  3. package/data/common/config/image_models_full.json +135 -48
  4. package/data/common/config/markup +1 -1
  5. package/data/common/config/story_models.json +41 -38
  6. package/data/common/prompts/character/progress_character.prompt +4 -0
  7. package/data/common/prompts/character/update_portrait.prompt +31 -0
  8. package/data/common/prompts/narrator/perceive.prompt +23 -1
  9. package/data/common/prompts/narrator/progress_plot.prompt +1 -3
  10. package/data/common/prompts/story_moderator/moderate.json +36 -29
  11. package/data/common/prompts/story_moderator/moderate.prompt +63 -18
  12. package/dist-sdk/server/src/ai/LLM/base-ai.service.d.ts +5 -7
  13. package/dist-sdk/server/src/ai/LLM/base-ai.service.js +7 -1
  14. package/dist-sdk/server/src/ai/LLM/fal.service.d.ts +5 -1
  15. package/dist-sdk/server/src/ai/LLM/fal.service.js +93 -21
  16. package/dist-sdk/server/src/ai/LLM/googleai.service.d.ts +1 -1
  17. package/dist-sdk/server/src/ai/LLM/googleai.service.js +20 -29
  18. package/dist-sdk/server/src/ai/LLM/openai.service.d.ts +5 -1
  19. package/dist-sdk/server/src/ai/LLM/openai.service.js +58 -71
  20. package/dist-sdk/server/src/ai/LLM/together.service.d.ts +5 -1
  21. package/dist-sdk/server/src/ai/LLM/together.service.js +57 -31
  22. package/dist-sdk/server/src/ai/agent-config.d.ts +1 -0
  23. package/dist-sdk/server/src/ai/agent-store.service.js +4 -0
  24. package/dist-sdk/server/src/ai/agent.d.ts +8 -1
  25. package/dist-sdk/server/src/ai/agent.js +21 -3
  26. package/dist-sdk/server/src/ai/model_prices.js +21 -1
  27. package/dist-sdk/server/src/app.controller.d.ts +4 -0
  28. package/dist-sdk/server/src/app.controller.js +51 -1
  29. package/dist-sdk/server/src/app.service.d.ts +3 -1
  30. package/dist-sdk/server/src/app.service.js +22 -14
  31. package/dist-sdk/server/src/room/message.d.ts +1 -0
  32. package/dist-sdk/server/src/room/message.js +1 -0
  33. package/dist-sdk/server/src/room/room-store.service.d.ts +1 -0
  34. package/dist-sdk/server/src/room/room-store.service.js +3 -0
  35. package/dist-sdk/server/src/room/room.d.ts +3 -0
  36. package/dist-sdk/server/src/room/room.js +27 -7
  37. package/dist-sdk/server/src/story/story-store.service.d.ts +1 -0
  38. package/dist-sdk/server/src/story/story-store.service.js +38 -0
  39. package/dist-sdk/server/src/story/story.service.d.ts +4 -1
  40. package/dist-sdk/server/src/story/story.service.js +233 -55
  41. package/dist-sdk/shared/types/app.types.d.ts +17 -0
  42. package/docs/assets/hierarchy.js +1 -1
  43. package/docs/assets/search.js +1 -1
  44. package/docs/classes/Agent.html +19 -15
  45. package/docs/classes/Briyah.html +12 -12
  46. package/docs/classes/BriyahConfigService.html +5 -5
  47. package/docs/classes/Room.html +26 -24
  48. package/docs/classes/RoomMessage.html +11 -10
  49. package/docs/enums/MessageAction.html +3 -3
  50. package/docs/hierarchy.html +1 -1
  51. package/docs/index.html +15 -3
  52. package/docs/interfaces/AgentInfo.html +2 -2
  53. package/docs/interfaces/AgentMessagesResponse.html +2 -2
  54. package/docs/interfaces/AppService.html +128 -113
  55. package/docs/interfaces/Artifact.html +3 -3
  56. package/docs/interfaces/ArtifactMetadata.html +2 -2
  57. package/docs/interfaces/AttachDocumentResponse.html +2 -2
  58. package/docs/interfaces/BriyahConfigOptions.html +6 -6
  59. package/docs/interfaces/ChapterInfo.html +2 -2
  60. package/docs/interfaces/Character.html +2 -2
  61. package/docs/interfaces/CreateAgentResponse.html +2 -2
  62. package/docs/interfaces/CreateRoomResponse.html +2 -2
  63. package/docs/interfaces/CreateStoryResponse.html +2 -2
  64. package/docs/interfaces/FileList.html +2 -2
  65. package/docs/interfaces/LoggingOptions.html +5 -5
  66. package/docs/interfaces/Message.html +2 -2
  67. package/docs/interfaces/ModelInfo.html +3 -2
  68. package/docs/interfaces/PreparedPromptResponse.html +2 -2
  69. package/docs/interfaces/ProcessTextResponse.html +2 -2
  70. package/docs/interfaces/PromptFile.html +2 -2
  71. package/docs/interfaces/PromptFileContent.html +2 -2
  72. package/docs/interfaces/PromptFilesResponse.html +2 -2
  73. package/docs/interfaces/PromptFolder.html +2 -2
  74. package/docs/interfaces/PromptFoldersResponse.html +2 -2
  75. package/docs/interfaces/RoomDetails.html +2 -2
  76. package/docs/interfaces/RoomInfo.html +2 -2
  77. package/docs/interfaces/RoomMessagesResponse.html +2 -2
  78. package/docs/interfaces/StoryErrorEvent.html +3 -3
  79. package/docs/interfaces/StoryIdea.html +2 -2
  80. package/docs/interfaces/StoryInfo.html +5 -3
  81. package/docs/interfaces/StoryIntroduceCharacterEvent.html +3 -3
  82. package/docs/interfaces/StoryProgressChapterEvent.html +3 -3
  83. package/docs/interfaces/StoryState.html +5 -5
  84. package/docs/interfaces/StoryStateEvent.html +3 -3
  85. package/docs/interfaces/Transaction.html +2 -2
  86. package/docs/interfaces/TransactionHistoryResponse.html +2 -2
  87. package/docs/modules.html +1 -1
  88. package/docs/types/PromptScope.html +1 -1
  89. package/package.json +1 -1
@@ -12,7 +12,11 @@ export declare class TogetherAiService extends BaseAiService {
12
12
  textPrompt(agent: Agent, prompt: string, jsonSchema?: any, saveResponse?: boolean, promptInstructions?: string, _cacheConfig?: any, maxOutputChars?: number): Promise<any>;
13
13
  private computeMessageCost;
14
14
  private computeImageCost;
15
- generateImage(agent: Agent, prompt: string, width: number, height: number, quality: string, referenceImageArtifactIds?: string[]): Promise<{
15
+ generateImage(agent: Agent, prompt: string, imageProperties?: Record<string, any>): Promise<{
16
+ artifactId?: string;
17
+ error?: any;
18
+ }>;
19
+ editImage(agent: Agent, prompt: string, imageProperties?: Record<string, any>, referenceImageArtifactIds?: string[]): Promise<{
16
20
  artifactId?: string;
17
21
  error?: any;
18
22
  }>;
@@ -182,10 +182,18 @@ let TogetherAiService = class TogetherAiService extends base_ai_service_1.BaseAi
182
182
  agent.balanceService.decrementBalance(cost, markup);
183
183
  }
184
184
  }
185
- computeImageCost(agent, usage) {
185
+ computeImageCost(agent, usage, centsPerImage) {
186
186
  const modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
187
187
  if (!modelInfo) {
188
- logger_1.logger.error(`No price info found for model ${agent.modelName}`);
188
+ if (centsPerImage != null) {
189
+ const cost = centsPerImage / 100;
190
+ agent.totalCost += cost;
191
+ logger_1.logger.log(`Image cost for ${agent.agentName}: $${cost.toFixed(4)}`);
192
+ if (agent.balanceService && cost > 0)
193
+ agent.balanceService.decrementBalance(cost, 0);
194
+ return;
195
+ }
196
+ logger_1.logger.warn(`No price info found for model ${agent.modelName}. Skipping cost tracking.`);
189
197
  return;
190
198
  }
191
199
  const inputImageTokens = usage.input_tokens_details?.image_tokens || 0;
@@ -235,7 +243,12 @@ let TogetherAiService = class TogetherAiService extends base_ai_service_1.BaseAi
235
243
  agent.balanceService.decrementBalance(cost, markup);
236
244
  }
237
245
  }
238
- async generateImage(agent, prompt, width, height, quality, referenceImageArtifactIds) {
246
+ async generateImage(agent, prompt, imageProperties) {
247
+ const { centsPerImage, ...apiProperties } = imageProperties ?? {};
248
+ const modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
249
+ if (!modelInfo && centsPerImage == null) {
250
+ return { error: `No pricing configured for image model "${agent.modelName}". Add a modelPrices entry or set centsPerImage in image_models.json.` };
251
+ }
239
252
  if (agent.balanceService && !agent.disableBalanceCheck) {
240
253
  const hasSufficientBalance = agent.balanceService.hasSufficientBalance();
241
254
  if (!hasSufficientBalance) {
@@ -243,34 +256,13 @@ let TogetherAiService = class TogetherAiService extends base_ai_service_1.BaseAi
243
256
  }
244
257
  }
245
258
  try {
246
- let imageResponse;
247
- if (referenceImageArtifactIds && referenceImageArtifactIds.length > 0) {
248
- const referenceImages = await this.loadReferenceImages(agent, referenceImageArtifactIds);
249
- imageResponse = await this.togetherClient.images.generate({
250
- reference_image: referenceImages.length === 1 ? referenceImages[0] : referenceImages,
251
- model: agent.modelName,
252
- prompt: prompt,
253
- height: height,
254
- width: width,
255
- n: 1,
256
- disable_safety_checker: true,
257
- response_format: 'base64',
258
- });
259
- }
260
- else {
261
- imageResponse = await this.togetherClient.images.generate({
262
- model: agent.modelName,
263
- prompt: prompt,
264
- height: height,
265
- width: width,
266
- n: 1,
267
- disable_safety_checker: true,
268
- response_format: 'base64',
269
- });
270
- }
271
- if (imageResponse.usage) {
272
- this.computeImageCost(agent, imageResponse.usage);
273
- }
259
+ const imageResponse = await this.togetherClient.images.generate({
260
+ ...apiProperties,
261
+ model: agent.modelName,
262
+ prompt,
263
+ n: 1,
264
+ });
265
+ this.computeImageCost(agent, imageResponse.usage, centsPerImage);
274
266
  const base64Data = imageResponse.data[0].b64_json;
275
267
  const artifactId = await this.saveImageAsArtifact(agent, base64Data, prompt);
276
268
  return { artifactId };
@@ -281,6 +273,40 @@ let TogetherAiService = class TogetherAiService extends base_ai_service_1.BaseAi
281
273
  return { error };
282
274
  }
283
275
  }
276
+ async editImage(agent, prompt, imageProperties, referenceImageArtifactIds) {
277
+ const { centsPerImage, ...apiProperties } = imageProperties ?? {};
278
+ const modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
279
+ if (!modelInfo && centsPerImage == null) {
280
+ return { error: `No pricing configured for image model "${agent.modelName}". Add a modelPrices entry or set centsPerImage in image_models.json.` };
281
+ }
282
+ if (agent.balanceService && !agent.disableBalanceCheck) {
283
+ const hasSufficientBalance = agent.balanceService.hasSufficientBalance();
284
+ if (!hasSufficientBalance) {
285
+ throw new errors_1.InsufficientBalanceError('Insufficient balance for image generation. Please add funds to continue.');
286
+ }
287
+ }
288
+ try {
289
+ const referenceImages = referenceImageArtifactIds?.length
290
+ ? await this.loadReferenceImages(agent, referenceImageArtifactIds)
291
+ : [];
292
+ const imageResponse = await this.togetherClient.images.generate({
293
+ ...apiProperties,
294
+ reference_image: referenceImages.length === 1 ? referenceImages[0] : referenceImages,
295
+ model: agent.modelName,
296
+ prompt,
297
+ n: 1,
298
+ });
299
+ this.computeImageCost(agent, imageResponse.usage, centsPerImage);
300
+ const base64Data = imageResponse.data[0].b64_json;
301
+ const artifactId = await this.saveImageAsArtifact(agent, base64Data, prompt);
302
+ return { artifactId };
303
+ }
304
+ catch (error) {
305
+ logger_1.logger.error('Error editing image:', error.error?.message || error.message);
306
+ logger_1.logger.log(prompt);
307
+ return { error };
308
+ }
309
+ }
284
310
  };
285
311
  exports.TogetherAiService = TogetherAiService;
286
312
  exports.TogetherAiService = TogetherAiService = __decorate([
@@ -7,6 +7,7 @@ export interface AgentConfig {
7
7
  promptFolder: string;
8
8
  systemInstruction: string;
9
9
  modelName: string;
10
+ smallModelName?: string;
10
11
  serviceName: string;
11
12
  reasoningEffort: 'low' | 'medium' | 'high' | null;
12
13
  allowSearch: boolean;
@@ -143,6 +143,7 @@ let AgentStoreService = class AgentStoreService {
143
143
  instance.maxHistoryMessages = template.maxHistoryMessages;
144
144
  instance.promptCacheTTL = template.promptCacheTTL;
145
145
  instance.markupRate = template.markupRate;
146
+ instance.smallModelName = template.smallModelName;
146
147
  this.storeAgent(instance);
147
148
  return instanceId;
148
149
  }
@@ -174,6 +175,7 @@ let AgentStoreService = class AgentStoreService {
174
175
  instance.promptFolder = template.promptFolder;
175
176
  instance.systemInstruction = template.systemInstruction;
176
177
  instance.modelName = template.modelName;
178
+ instance.smallModelName = template.smallModelName;
177
179
  instance.serviceName = template.serviceName;
178
180
  instance.reasoningEffort = template.reasoningEffort;
179
181
  instance.allowSearch = template.allowSearch;
@@ -234,6 +236,7 @@ let AgentStoreService = class AgentStoreService {
234
236
  const agent = new agent_1.Agent(config.id, this.configService, this, this.balanceService, finalArtifactService, config.agentName, config.agentNickname || config.agentName, config.description, config.promptFolder, config.systemInstruction, config.history || [], config.modelName, config.serviceName, aiService, config.reasoningEffort, config.allowSearch, config.maxOutputTokens, config.beginInstruction || '', storageDir);
235
237
  agent.createdAt = new Date(config.createdAt);
236
238
  agent.configService = this.configService;
239
+ agent.smallModelName = config.smallModelName;
237
240
  agent.totalInputTokens = config.totalInputTokens || 0;
238
241
  agent.totalOutputTokens = config.totalOutputTokens || 0;
239
242
  agent.totalCost = config.totalCost || 0;
@@ -305,6 +308,7 @@ let AgentStoreService = class AgentStoreService {
305
308
  promptFolder: agent.promptFolder,
306
309
  systemInstruction: agent.systemInstruction,
307
310
  modelName: agent.modelName,
311
+ smallModelName: agent.smallModelName,
308
312
  serviceName: agent.serviceName,
309
313
  reasoningEffort: agent.reasoningEffort,
310
314
  allowSearch: agent.allowSearch,
@@ -38,7 +38,9 @@ export declare class Agent {
38
38
  systemInstruction: string;
39
39
  maxHistoryMessages?: number;
40
40
  modelName: string;
41
+ smallModelName?: string;
41
42
  serviceName: string;
43
+ imageProperties?: Record<string, any>;
42
44
  reasoningEffort: 'low' | 'medium' | 'high' | null;
43
45
  allowSearch: boolean;
44
46
  maxOutputTokens: number;
@@ -67,6 +69,7 @@ export declare class Agent {
67
69
  trimHistoryIfNeeded(threshold?: number): void;
68
70
  instructedPrompt(prompt: string, instructionFileName: string, variables: any, saveResponse?: boolean, cacheMessage?: boolean, maxOutputChars?: number): Promise<any>;
69
71
  preparedPrompt(promptFileName: string, variables: any, saveResponse?: boolean, cacheMessage?: boolean): Promise<any>;
72
+ withSmallModel<T>(fn: () => Promise<T>): Promise<T>;
70
73
  compact(): Promise<{
71
74
  success: boolean;
72
75
  originalLength: number;
@@ -75,7 +78,11 @@ export declare class Agent {
75
78
  summary?: string;
76
79
  }>;
77
80
  textPrompt(prompt: string, jsonSchema?: any, saveResponse?: boolean, promptInstructions?: string, cacheMessage?: boolean, maxOutputChars?: number): Promise<any>;
78
- generateImage(prompt: string, width: number, height: number, quality: string, referenceImageArtifactIds?: string[]): Promise<{
81
+ generateImage(prompt: string): Promise<{
82
+ artifactId?: string;
83
+ error?: any;
84
+ }>;
85
+ editImage(prompt: string, referenceImageArtifactIds?: string[]): Promise<{
79
86
  artifactId?: string;
80
87
  error?: any;
81
88
  }>;
@@ -36,7 +36,9 @@ class Agent {
36
36
  systemInstruction;
37
37
  maxHistoryMessages;
38
38
  modelName;
39
+ smallModelName;
39
40
  serviceName;
41
+ imageProperties;
40
42
  reasoningEffort;
41
43
  allowSearch;
42
44
  maxOutputTokens = 0;
@@ -164,6 +166,19 @@ class Agent {
164
166
  async preparedPrompt(promptFileName, variables, saveResponse = true, cacheMessage = false) {
165
167
  return await this.aiService.preparedPrompt(this, promptFileName, variables, saveResponse, this.aiService.getCacheConfig(this, cacheMessage));
166
168
  }
169
+ async withSmallModel(fn) {
170
+ if (!this.smallModelName || this.smallModelName === this.modelName) {
171
+ return fn();
172
+ }
173
+ const original = this.modelName;
174
+ this.modelName = this.smallModelName;
175
+ try {
176
+ return await fn();
177
+ }
178
+ finally {
179
+ this.modelName = original;
180
+ }
181
+ }
167
182
  async compact() {
168
183
  const originalHistory = [...(this.history || [])];
169
184
  const originalLength = originalHistory.length;
@@ -178,7 +193,7 @@ class Agent {
178
193
  const formattedHistory = this.getFormattedMessages(0);
179
194
  try {
180
195
  this.history = [];
181
- const summaryResponse = await this.preparedPrompt('compact_agent', { agentName: this.agentNickname, formattedHistory }, false);
196
+ const summaryResponse = await this.withSmallModel(() => this.preparedPrompt('compact_agent', { agentName: this.agentNickname, formattedHistory }, false));
182
197
  const summary = summaryResponse?.summary || summaryResponse;
183
198
  this.addToConversationHistory(`[CONVERSATION HISTORY SUMMARY]\n\n${summary}`, true);
184
199
  const newLength = this.history.length;
@@ -199,8 +214,11 @@ class Agent {
199
214
  async textPrompt(prompt, jsonSchema = null, saveResponse = true, promptInstructions = null, cacheMessage = false, maxOutputChars = 0) {
200
215
  return await this.aiService.textPrompt(this, prompt, jsonSchema, saveResponse, promptInstructions, this.aiService.getCacheConfig(this, cacheMessage), maxOutputChars);
201
216
  }
202
- async generateImage(prompt, width, height, quality, referenceImageArtifactIds) {
203
- return await this.aiService.generateImage(this, prompt, width, height, quality, referenceImageArtifactIds);
217
+ async generateImage(prompt) {
218
+ return await this.aiService.generateImage(this, prompt, this.imageProperties);
219
+ }
220
+ async editImage(prompt, referenceImageArtifactIds) {
221
+ return await this.aiService.editImage(this, prompt, this.imageProperties, referenceImageArtifactIds);
204
222
  }
205
223
  }
206
224
  exports.Agent = Agent;
@@ -39,6 +39,24 @@ exports.isKnownModel = isKnownModel;
39
39
  const fs = __importStar(require("fs"));
40
40
  const path = __importStar(require("path"));
41
41
  let _modelPrices = null;
42
+ let _imageModels = null;
43
+ function getImageModels() {
44
+ if (_imageModels !== null)
45
+ return _imageModels;
46
+ const dataDir = process.env.BRIYAH_DATA_PATH || path.resolve(process.cwd(), 'briyah-data');
47
+ const imageModelsPath = path.join(dataDir, 'common', 'config', 'image_models.json');
48
+ if (!fs.existsSync(imageModelsPath)) {
49
+ _imageModels = [];
50
+ return _imageModels;
51
+ }
52
+ try {
53
+ _imageModels = JSON.parse(fs.readFileSync(imageModelsPath, 'utf8'));
54
+ }
55
+ catch {
56
+ _imageModels = [];
57
+ }
58
+ return _imageModels;
59
+ }
42
60
  function getModelPrices() {
43
61
  if (_modelPrices !== null)
44
62
  return _modelPrices;
@@ -69,5 +87,7 @@ function resolveModelInfo(modelName, prefix) {
69
87
  return null;
70
88
  }
71
89
  function isKnownModel(modelName, prefix) {
72
- return resolveModelInfo(modelName, prefix) !== null;
90
+ if (resolveModelInfo(modelName, prefix) !== null)
91
+ return true;
92
+ return getImageModels().some((m) => m.model === modelName && m.centsPerImage != null);
73
93
  }
@@ -129,6 +129,7 @@ export declare class AppController {
129
129
  getPublishedRoomInstances(req: ExpressRequest, templateId: string): Promise<RoomInfo[]>;
130
130
  resetPublishedRoomInstance(req: ExpressRequest, instanceId: string): Promise<void>;
131
131
  resetStory(req: ExpressRequest, storyId: string): Promise<void>;
132
+ revertStoryChapter(req: ExpressRequest, storyId: string): Promise<void>;
132
133
  getRoomArtifacts(req: ExpressRequest, roomId: string): Promise<{
133
134
  artifacts: any[];
134
135
  }>;
@@ -150,9 +151,11 @@ export declare class AppController {
150
151
  idea: string;
151
152
  userCharacterDesc: string;
152
153
  otherCharactersDesc: string;
154
+ illustrateStory: boolean;
153
155
  storyModel?: string;
154
156
  isImport?: boolean;
155
157
  imageModelName?: string;
158
+ imageEditModelName?: string;
156
159
  }): Promise<StoryInfo>;
157
160
  importStoryZip(req: ExpressRequest, file: Express.Multer.File): Promise<StoryInfo>;
158
161
  deleteStory(req: ExpressRequest, storyId: string): Promise<void>;
@@ -164,6 +167,7 @@ export declare class AppController {
164
167
  respondToStory(req: ExpressRequest, storyId: string, body: {
165
168
  content: string;
166
169
  }): Promise<void>;
170
+ interruptStory(req: ExpressRequest, storyId: string): Promise<void>;
167
171
  introduceCharacter(req: ExpressRequest, storyId: string, body: {
168
172
  name: string;
169
173
  description?: string;
@@ -132,6 +132,7 @@ let AppController = class AppController {
132
132
  return {
133
133
  selectedStoryModel: process.env.DEFAULT_STORY_MODEL,
134
134
  selectedImageModel: process.env.DEFAULT_IMAGE_MODEL,
135
+ selectedImageEditModel: process.env.DEFAULT_IMAGE_EDIT_MODEL,
135
136
  enforceDefaultModels: process.env.ENFORCE_DEFAULT_MODELS === 'true',
136
137
  };
137
138
  }
@@ -1040,6 +1041,25 @@ let AppController = class AppController {
1040
1041
  throw new common_1.HttpException('Failed to reset story', common_1.HttpStatus.INTERNAL_SERVER_ERROR);
1041
1042
  }
1042
1043
  }
1044
+ async revertStoryChapter(req, storyId) {
1045
+ if (!storyId) {
1046
+ throw new common_1.BadRequestException('Story ID is required');
1047
+ }
1048
+ try {
1049
+ const appService = this.getAppService(req);
1050
+ await appService.revertStoryChapter(storyId);
1051
+ }
1052
+ catch (error) {
1053
+ logger_1.logger.error(`[${req.user?.sub}] Error reverting chapter for story ${storyId}:`, error);
1054
+ if (error instanceof errors_1.NotFoundError) {
1055
+ throw new common_1.NotFoundException(error.message);
1056
+ }
1057
+ if (error instanceof errors_1.OperationFailedError) {
1058
+ throw new common_1.HttpException(error.message, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
1059
+ }
1060
+ throw new common_1.HttpException('Failed to revert chapter', common_1.HttpStatus.INTERNAL_SERVER_ERROR);
1061
+ }
1062
+ }
1043
1063
  async getRoomArtifacts(req, roomId) {
1044
1064
  try {
1045
1065
  const appService = this.getAppService(req, undefined, roomId);
@@ -1134,7 +1154,7 @@ let AppController = class AppController {
1134
1154
  }
1135
1155
  try {
1136
1156
  const appService = this.getAppService(req);
1137
- return appService.createStory(body.name, body.idea, body.userCharacterDesc, body.otherCharactersDesc, body.storyModel, body.isImport, body.imageModelName);
1157
+ return appService.createStory(body.name, body.idea, body.userCharacterDesc, body.otherCharactersDesc, body.illustrateStory ?? true, body.storyModel, body.isImport, body.imageModelName, body.imageEditModelName);
1138
1158
  }
1139
1159
  catch (error) {
1140
1160
  logger_1.logger.error(`[${req.user?.sub}] Error creating story ${body.name}:`, error);
@@ -1255,6 +1275,19 @@ let AppController = class AppController {
1255
1275
  throw new common_1.HttpException(error.message || 'Failed to respond to story', common_1.HttpStatus.INTERNAL_SERVER_ERROR);
1256
1276
  }
1257
1277
  }
1278
+ async interruptStory(req, storyId) {
1279
+ try {
1280
+ const appService = this.getAppService(req);
1281
+ await appService.interruptStory(storyId);
1282
+ }
1283
+ catch (error) {
1284
+ logger_1.logger.error(`[${req.user?.sub}] Error interrupting story ${storyId}:`, error);
1285
+ if (error instanceof common_1.HttpException) {
1286
+ throw error;
1287
+ }
1288
+ throw new common_1.HttpException(error.message || 'Failed to interrupt story', common_1.HttpStatus.INTERNAL_SERVER_ERROR);
1289
+ }
1290
+ }
1258
1291
  async introduceCharacter(req, storyId, body) {
1259
1292
  if (!body.name) {
1260
1293
  throw new common_1.BadRequestException('Name is a required field');
@@ -2325,6 +2358,15 @@ __decorate([
2325
2358
  __metadata("design:paramtypes", [Object, String]),
2326
2359
  __metadata("design:returntype", Promise)
2327
2360
  ], AppController.prototype, "resetStory", null);
2361
+ __decorate([
2362
+ (0, common_1.Post)('stories/:storyId/revert'),
2363
+ (0, common_1.HttpCode)(common_1.HttpStatus.NO_CONTENT),
2364
+ __param(0, (0, common_1.Request)()),
2365
+ __param(1, (0, common_1.Param)('storyId')),
2366
+ __metadata("design:type", Function),
2367
+ __metadata("design:paramtypes", [Object, String]),
2368
+ __metadata("design:returntype", Promise)
2369
+ ], AppController.prototype, "revertStoryChapter", null);
2328
2370
  __decorate([
2329
2371
  (0, room_access_decorator_1.RoomReadAccess)(),
2330
2372
  (0, common_1.Get)('rooms/:roomId/artifacts'),
@@ -2448,6 +2490,14 @@ __decorate([
2448
2490
  __metadata("design:paramtypes", [Object, String, Object]),
2449
2491
  __metadata("design:returntype", Promise)
2450
2492
  ], AppController.prototype, "respondToStory", null);
2493
+ __decorate([
2494
+ (0, common_1.Post)('stories/:storyId/interrupt'),
2495
+ __param(0, (0, common_1.Request)()),
2496
+ __param(1, (0, common_1.Param)('storyId')),
2497
+ __metadata("design:type", Function),
2498
+ __metadata("design:paramtypes", [Object, String]),
2499
+ __metadata("design:returntype", Promise)
2500
+ ], AppController.prototype, "interruptStory", null);
2451
2501
  __decorate([
2452
2502
  (0, common_1.Post)('stories/:storyId/characters'),
2453
2503
  (0, common_1.HttpCode)(common_1.HttpStatus.CREATED),
@@ -115,7 +115,8 @@ export declare class AppService {
115
115
  compactAgentConversation(agentId: string): Promise<void>;
116
116
  resetRoom(roomId: string): Promise<void>;
117
117
  resetStory(storyId: string): Promise<void>;
118
- createStory(name: string, idea: string, userCharacterDesc: string, otherCharactersDesc: string, storyModel?: string, isImport?: boolean, imageModelName?: string): Promise<StoryInfo>;
118
+ revertStoryChapter(storyId: string): Promise<void>;
119
+ createStory(name: string, idea: string, userCharacterDesc: string, otherCharactersDesc: string, illustrateStory: boolean, storyModel?: string, isImport?: boolean, imageModelName?: string, imageEditModelName?: string): Promise<StoryInfo>;
119
120
  importStoryFromZip(zipBuffer: Buffer): Promise<StoryInfo>;
120
121
  getStoryProgressEmitter(storyId: string): EventEmitter<any>;
121
122
  getStoryMessageEmitter(storyId: string): EventEmitter<any>;
@@ -140,6 +141,7 @@ export declare class AppService {
140
141
  getStoryState(storyId: string): Promise<StoryState>;
141
142
  getStoryInfo(storyId: string): Promise<StoryInfo | undefined>;
142
143
  respondToStory(storyId: string, content: string): Promise<void>;
144
+ interruptStory(storyId: string): Promise<void>;
143
145
  introduceCharacterToStory(storyId: string, name: string, description: string, storyModel?: string, fromNarratorSuggestion?: boolean): Promise<void>;
144
146
  declineCharacter(storyId: string, characterName: string): Promise<void>;
145
147
  deleteCharacterFromStory(storyId: string, characterName: string): Promise<void>;
@@ -694,8 +694,7 @@ class AppService {
694
694
  const allFiles = fs.readdirSync(targetDir);
695
695
  const promptFiles = allFiles.filter((file) => file.endsWith('.prompt') || file.endsWith('.json'));
696
696
  promptFiles.forEach((file) => {
697
- const baseName = file.replace(/\.(prompt|json)$/, '');
698
- fileMap.set(baseName, scope);
697
+ fileMap.set(file, scope);
699
698
  });
700
699
  };
701
700
  scanDirectory(this.configService.getCommonPromptsDir(), 'common');
@@ -749,17 +748,20 @@ class AppService {
749
748
  fs.writeFileSync(filePath, content);
750
749
  }
751
750
  deletePromptFile(folderName, fileName) {
752
- let targetDir = this.configService.getPromptsDir();
753
- if (folderName !== 'shared') {
754
- targetDir = path.join(this.configService.getPromptsDir(), folderName);
755
- }
756
- const promptPath = path.join(targetDir, fileName + '.prompt');
757
- if (fs.existsSync(promptPath)) {
758
- fs.unlinkSync(promptPath);
751
+ const baseDir = this.configService.getUserPromptsDir();
752
+ const targetDir = folderName === 'shared' ? baseDir : path.join(baseDir, folderName);
753
+ const hasExtension = fileName.endsWith('.prompt') || fileName.endsWith('.json');
754
+ if (hasExtension) {
755
+ const filePath = path.join(targetDir, fileName);
756
+ if (fs.existsSync(filePath))
757
+ fs.unlinkSync(filePath);
759
758
  }
760
- const jsonPath = path.join(targetDir, fileName + '.json');
761
- if (fs.existsSync(jsonPath)) {
762
- fs.unlinkSync(jsonPath);
759
+ else {
760
+ for (const ext of ['.prompt', '.json']) {
761
+ const filePath = path.join(targetDir, fileName + ext);
762
+ if (fs.existsSync(filePath))
763
+ fs.unlinkSync(filePath);
764
+ }
763
765
  }
764
766
  }
765
767
  async getRoomArtifacts(roomId) {
@@ -967,8 +969,11 @@ class AppService {
967
969
  logger_1.logger.warn(`Story "${storyName}" reset with warnings:`, warnings);
968
970
  }
969
971
  }
970
- async createStory(name, idea, userCharacterDesc, otherCharactersDesc, storyModel, isImport, imageModelName) {
971
- return this.storyService.createStory(name, idea, userCharacterDesc, otherCharactersDesc, storyModel, isImport, imageModelName);
972
+ async revertStoryChapter(storyId) {
973
+ return this.storyService.revertStoryChapter(storyId);
974
+ }
975
+ async createStory(name, idea, userCharacterDesc, otherCharactersDesc, illustrateStory, storyModel, isImport, imageModelName, imageEditModelName) {
976
+ return this.storyService.createStory(name, idea, userCharacterDesc, otherCharactersDesc, illustrateStory, storyModel, isImport, imageModelName, imageEditModelName);
972
977
  }
973
978
  async importStoryFromZip(zipBuffer) {
974
979
  return this.storyService.importStoryFromZip(zipBuffer);
@@ -1102,6 +1107,9 @@ class AppService {
1102
1107
  async respondToStory(storyId, content) {
1103
1108
  return this.storyService.respondToStory(storyId, content);
1104
1109
  }
1110
+ async interruptStory(storyId) {
1111
+ return this.storyService.interruptStory(storyId);
1112
+ }
1105
1113
  async introduceCharacterToStory(storyId, name, description, storyModel, fromNarratorSuggestion) {
1106
1114
  await this.storyService.introduceCharacter(storyId, name, description, storyModel, fromNarratorSuggestion);
1107
1115
  }
@@ -7,6 +7,7 @@ export declare class RoomMessage implements Message {
7
7
  timestamp: Date;
8
8
  name?: string;
9
9
  situation?: string;
10
+ updatePortrait?: string[];
10
11
  index?: number;
11
12
  constructor(sender: string, action: MessageAction, content: string, targets?: string[], name?: string, situation?: string);
12
13
  getSender(): string;
@@ -10,6 +10,7 @@ class RoomMessage {
10
10
  timestamp;
11
11
  name;
12
12
  situation;
13
+ updatePortrait;
13
14
  index;
14
15
  constructor(sender, action = app_types_1.MessageAction.SPEAK, content, targets = [], name, situation) {
15
16
  this.sender = sender;
@@ -34,5 +34,6 @@ export declare class RoomStoreService {
34
34
  saveArtifactFile(roomId: string, storageDir: string, artifact: Artifact): void;
35
35
  deleteArtifactFile(roomId: string, storageDir: string, artifactName: string): boolean;
36
36
  deleteAllArtifactFiles(roomId: string, storageDir: string): void;
37
+ loadArtifactFiles(roomId: string, storageDir: string): Artifact[];
37
38
  private deleteRoomFile;
38
39
  }
@@ -318,6 +318,9 @@ let RoomStoreService = class RoomStoreService {
318
318
  deleteAllArtifactFiles(roomId, storageDir) {
319
319
  this.artifactStoreService.deleteAllArtifacts(roomId, storageDir);
320
320
  }
321
+ loadArtifactFiles(roomId, storageDir) {
322
+ return this.artifactStoreService.loadArtifacts(roomId, storageDir);
323
+ }
321
324
  deleteRoomFile(roomId, storageDir, deleteAgents = false, room) {
322
325
  try {
323
326
  if (deleteAgents && room) {
@@ -23,6 +23,7 @@ export declare class Room {
23
23
  private onError?;
24
24
  private currentSituation;
25
25
  private onSituationUpdate?;
26
+ private onModeratorResponseCallback?;
26
27
  private templateRoomId;
27
28
  private storageDir;
28
29
  private roomStoreService;
@@ -93,6 +94,7 @@ export declare class Room {
93
94
  getSituation(): string;
94
95
  setSituation(situation: string): void;
95
96
  setOnSituationUpdate(callback: (situation: string) => void): void;
97
+ setOnModeratorResponse(callback: (message: RoomMessage) => void): void;
96
98
  private handleSituationUpdate;
97
99
  handlePublishMessages(messages: RoomMessage[]): void;
98
100
  handleExecuteMessages(messages: RoomMessage[]): void;
@@ -102,6 +104,7 @@ export declare class Room {
102
104
  isProcessingInProgress(): boolean;
103
105
  pause(): void;
104
106
  resume(): void;
107
+ interrupt(): void;
105
108
  checkAgentNicknameMatch(existingNickname: string, targetNickname: string): boolean;
106
109
  private handleModeratorResponse;
107
110
  private processPendingMessages;
@@ -29,6 +29,7 @@ class Room {
29
29
  onError;
30
30
  currentSituation = '';
31
31
  onSituationUpdate;
32
+ onModeratorResponseCallback;
32
33
  templateRoomId;
33
34
  storageDir;
34
35
  roomStoreService;
@@ -242,7 +243,11 @@ class Room {
242
243
  return new message_1.RoomMessage(agent.agentNickname, app_types_1.MessageAction.MODERATE, content, [], null, situation);
243
244
  }
244
245
  situation = response.situation || undefined;
245
- return new message_1.RoomMessage(agent.agentNickname, normalizedAction, response.content, targets, response.name, situation);
246
+ const msg = new message_1.RoomMessage(agent.agentNickname, normalizedAction, response.content, targets, response.name, situation);
247
+ if (Array.isArray(response.update_portrait) && response.update_portrait.length > 0) {
248
+ msg.updatePortrait = response.update_portrait;
249
+ }
250
+ return msg;
246
251
  }
247
252
  static coerceMessageAction(raw) {
248
253
  if (typeof raw !== 'string')
@@ -484,6 +489,9 @@ Recent Message: ${message.getContent()}
484
489
  setOnSituationUpdate(callback) {
485
490
  this.onSituationUpdate = callback;
486
491
  }
492
+ setOnModeratorResponse(callback) {
493
+ this.onModeratorResponseCallback = callback;
494
+ }
487
495
  handleSituationUpdate(situation) {
488
496
  logger_1.logger.log(`Situation updated: ${situation}`);
489
497
  this.currentSituation = situation;
@@ -610,6 +618,10 @@ Recent Message: ${message.getContent()}
610
618
  this.processPendingMessages();
611
619
  }
612
620
  }
621
+ interrupt() {
622
+ this.pendingMessages = [];
623
+ this.pause();
624
+ }
613
625
  checkAgentNicknameMatch(existingNickname, targetNickname) {
614
626
  if (existingNickname.toLowerCase() === targetNickname.toLowerCase())
615
627
  return true;
@@ -625,6 +637,9 @@ Recent Message: ${message.getContent()}
625
637
  const targets = moderatorMessage.targets || [];
626
638
  if (!targets.includes(roomLeaderName))
627
639
  targets.push(roomLeaderName);
640
+ if (this.onModeratorResponseCallback) {
641
+ this.onModeratorResponseCallback(moderatorMessage);
642
+ }
628
643
  let content = moderatorMessage.content;
629
644
  if (content && content.startsWith('relay'))
630
645
  content = originalMessage.getContent();
@@ -659,13 +674,8 @@ Recent Message: ${message.getContent()}
659
674
  let roomLeaderAgent = this.agents.find((a) => a.agentNickname === this.getRoomLeader());
660
675
  if (!roomLeaderAgent)
661
676
  throw new Error(`Room leader agent not found`);
662
- logger_1.logger.warn(`Responder agent not found: ${responderNickname}`);
677
+ logger_1.logger.warn(`Responder agent "${responderNickname}" not found; falling back to room leader: ${roomLeaderAgent.agentNickname}`);
663
678
  responderAgent = roomLeaderAgent;
664
- if (responderNickname) {
665
- if (this.onSituationUpdate && responderNickname.length > 0) {
666
- this.onSituationUpdate(`INTRODUCE: ${responderNickname}`);
667
- }
668
- }
669
679
  }
670
680
  if (responderAgent.isControlledByHuman) {
671
681
  logger_1.logger.log(`Waiting for human agent ${responderNickname} to respond`);
@@ -678,6 +688,11 @@ Recent Message: ${message.getContent()}
678
688
  }
679
689
  this.setCurrentSpeaker(responderNickname);
680
690
  this.perceive(responderAgent, originalMessage, (response) => {
691
+ if (this.isPaused) {
692
+ this.processingInProgress = false;
693
+ this.notifyStateChange();
694
+ return;
695
+ }
681
696
  if (response !== null) {
682
697
  this.pendingMessages = [response, ...this.pendingMessages];
683
698
  }
@@ -738,6 +753,11 @@ Recent Message: ${message.getContent()}
738
753
  this.pendingMessages = [...moderateMessages.slice(1), ...this.pendingMessages];
739
754
  }
740
755
  this.moderate(moderator, originalMessage, (response) => {
756
+ if (this.isPaused) {
757
+ this.processingInProgress = false;
758
+ this.notifyStateChange();
759
+ return;
760
+ }
741
761
  if (response !== null) {
742
762
  this.handleModeratorResponse(response, originalMessage);
743
763
  }
@@ -19,5 +19,6 @@ export declare class StoryStoreService {
19
19
  createChapterBackup(storyId: string, chapterIndex: number, chapterTitle: string): boolean;
20
20
  saveChapterContent(storyId: string, chapterIndex: number, content: string): boolean;
21
21
  deleteChapter(storyId: string, chapterIndex: number): boolean;
22
+ restoreRoomFilesFromChapter(storyId: string, chapterIndex: number): number;
22
23
  restoreAgentsFromChapter(storyId: string, chapterIndex: number, agentStore: any): Promise<string[]>;
23
24
  }