briyah 1.2.3 → 1.2.5

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 (119) hide show
  1. package/README.md +55 -6
  2. package/data/common/config/image_models.json +160 -97
  3. package/data/common/config/markup +1 -1
  4. package/data/common/config/model_prices.json +31 -0
  5. package/data/common/config/story_models.json +31 -11
  6. package/data/common/config/story_models_full.json +25 -7
  7. package/data/common/prompts/character/create_user_character.prompt +3 -12
  8. package/data/common/prompts/character/introduce_character.json +18 -0
  9. package/data/common/prompts/character/introduce_character.mock +4 -0
  10. package/data/common/prompts/character/introduce_character.prompt +58 -0
  11. package/data/common/prompts/character/perceive.prompt +3 -0
  12. package/data/common/prompts/character/progress_character.json +8 -4
  13. package/data/common/prompts/character/progress_character.mock +1 -0
  14. package/data/common/prompts/character/progress_character.prompt +20 -2
  15. package/data/common/prompts/character/update_portrait.json +18 -0
  16. package/data/common/prompts/character/update_portrait.mock +4 -0
  17. package/data/common/prompts/character/update_portrait.prompt +17 -1
  18. package/data/common/prompts/narrator/create_simple_plot.json +0 -0
  19. package/data/common/prompts/narrator/create_simple_plot.mock +13 -0
  20. package/data/common/prompts/narrator/create_simple_plot.prompt +35 -0
  21. package/data/common/prompts/narrator/perceive.prompt +12 -9
  22. package/data/common/prompts/narrator/progress_simple_plot.json +0 -0
  23. package/data/common/prompts/narrator/progress_simple_plot.mock +13 -0
  24. package/data/common/prompts/narrator/progress_simple_plot.prompt +40 -0
  25. package/data/common/prompts/perceive.json +1 -1
  26. package/data/common/prompts/perceive.prompt +82 -20
  27. package/data/common/prompts/story_moderator/moderate.json +1 -1
  28. package/data/common/prompts/story_moderator/moderate.prompt +26 -6
  29. package/dist-sdk/server/src/ai/LLM/anthropic.service.js +6 -4
  30. package/dist-sdk/server/src/ai/LLM/base-ai.service.d.ts +1 -1
  31. package/dist-sdk/server/src/ai/LLM/base-ai.service.js +13 -42
  32. package/dist-sdk/server/src/ai/LLM/deepseek.service.js +10 -1
  33. package/dist-sdk/server/src/ai/LLM/fal.service.js +1 -2
  34. package/dist-sdk/server/src/ai/LLM/googleai.service.js +1 -2
  35. package/dist-sdk/server/src/ai/LLM/grok.service.js +1 -2
  36. package/dist-sdk/server/src/ai/LLM/openai.service.js +1 -2
  37. package/dist-sdk/server/src/ai/LLM/together.service.js +1 -2
  38. package/dist-sdk/server/src/ai/agent-config.d.ts +2 -0
  39. package/dist-sdk/server/src/ai/agent-store.service.js +8 -0
  40. package/dist-sdk/server/src/ai/agent.d.ts +2 -0
  41. package/dist-sdk/server/src/ai/agent.js +2 -0
  42. package/dist-sdk/server/src/ai/model_prices.js +2 -1
  43. package/dist-sdk/server/src/app/user-service-factory.js +7 -3
  44. package/dist-sdk/server/src/app.controller.d.ts +15 -4
  45. package/dist-sdk/server/src/app.controller.js +171 -5
  46. package/dist-sdk/server/src/app.service.d.ts +19 -5
  47. package/dist-sdk/server/src/app.service.js +50 -4
  48. package/dist-sdk/server/src/room/message.js +5 -1
  49. package/dist-sdk/server/src/room/room-factory.d.ts +5 -1
  50. package/dist-sdk/server/src/room/room-factory.js +6 -1
  51. package/dist-sdk/server/src/room/room-store.service.d.ts +5 -1
  52. package/dist-sdk/server/src/room/room-store.service.js +13 -2
  53. package/dist-sdk/server/src/room/room.d.ts +25 -4
  54. package/dist-sdk/server/src/room/room.js +393 -96
  55. package/dist-sdk/server/src/sdk/index.d.ts +1 -1
  56. package/dist-sdk/server/src/story/story.service.d.ts +5 -4
  57. package/dist-sdk/server/src/story/story.service.js +207 -120
  58. package/dist-sdk/server/src/tools/tool-execution.service.d.ts +19 -0
  59. package/dist-sdk/server/src/tools/tool-execution.service.js +100 -0
  60. package/dist-sdk/server/src/tools/tool-store.service.d.ts +17 -0
  61. package/dist-sdk/server/src/tools/tool-store.service.js +143 -0
  62. package/dist-sdk/shared/types/app.types.d.ts +44 -5
  63. package/dist-sdk/shared/types/app.types.js +3 -0
  64. package/docs/assets/hierarchy.js +1 -1
  65. package/docs/assets/highlight.css +0 -7
  66. package/docs/assets/navigation.js +1 -1
  67. package/docs/assets/search.js +1 -1
  68. package/docs/classes/Agent.html +22 -14
  69. package/docs/classes/Briyah.html +10 -10
  70. package/docs/classes/BriyahConfigService.html +5 -5
  71. package/docs/classes/Room.html +32 -26
  72. package/docs/classes/RoomMessage.html +10 -10
  73. package/docs/enums/MessageAction.html +6 -3
  74. package/docs/hierarchy.html +1 -1
  75. package/docs/index.html +12 -5
  76. package/docs/interfaces/AgentInfo.html +3 -2
  77. package/docs/interfaces/AgentMessagesResponse.html +2 -2
  78. package/docs/interfaces/AppService.html +184 -149
  79. package/docs/interfaces/Artifact.html +3 -3
  80. package/docs/interfaces/ArtifactMetadata.html +2 -2
  81. package/docs/interfaces/AttachDocumentResponse.html +2 -2
  82. package/docs/interfaces/BriyahConfigOptions.html +7 -7
  83. package/docs/interfaces/ChapterInfo.html +2 -2
  84. package/docs/interfaces/Character.html +2 -2
  85. package/docs/interfaces/CreateAgentResponse.html +2 -2
  86. package/docs/interfaces/CreateRoomResponse.html +2 -2
  87. package/docs/interfaces/CreateStoryResponse.html +2 -2
  88. package/docs/interfaces/FileList.html +2 -2
  89. package/docs/interfaces/FileMetadata.html +3 -3
  90. package/docs/interfaces/IConfigService.html +3 -3
  91. package/docs/interfaces/LoggingOptions.html +6 -6
  92. package/docs/interfaces/Message.html +2 -2
  93. package/docs/interfaces/ModelInfo.html +2 -3
  94. package/docs/interfaces/PreparedPromptResponse.html +2 -2
  95. package/docs/interfaces/ProcessTextResponse.html +2 -2
  96. package/docs/interfaces/PromptFile.html +2 -2
  97. package/docs/interfaces/PromptFileContent.html +2 -2
  98. package/docs/interfaces/PromptFilesResponse.html +2 -2
  99. package/docs/interfaces/PromptFolder.html +2 -2
  100. package/docs/interfaces/PromptFoldersResponse.html +2 -2
  101. package/docs/interfaces/RoomDetails.html +2 -2
  102. package/docs/interfaces/RoomInfo.html +2 -2
  103. package/docs/interfaces/RoomMessagesResponse.html +2 -2
  104. package/docs/interfaces/StoryErrorEvent.html +3 -3
  105. package/docs/interfaces/StoryIdea.html +2 -2
  106. package/docs/interfaces/StoryInfo.html +5 -4
  107. package/docs/interfaces/StoryIntroduceCharacterEvent.html +3 -3
  108. package/docs/interfaces/StoryProgressChapterEvent.html +3 -3
  109. package/docs/interfaces/StoryState.html +5 -5
  110. package/docs/interfaces/StoryStateEvent.html +3 -3
  111. package/docs/interfaces/ToolDefinition.html +6 -0
  112. package/docs/interfaces/ToolParameter.html +5 -0
  113. package/docs/interfaces/ToolsResponse.html +2 -0
  114. package/docs/interfaces/Transaction.html +2 -2
  115. package/docs/interfaces/TransactionHistoryResponse.html +2 -2
  116. package/docs/modules.html +1 -1
  117. package/docs/types/PromptScope.html +1 -1
  118. package/docs/types/ToolRunResult.html +1 -0
  119. package/package.json +2 -1
@@ -192,6 +192,8 @@ let StoryService = class StoryService {
192
192
  service: modelConfig.service,
193
193
  model: modelConfig.model,
194
194
  smallModel: modelConfig.small_model,
195
+ moderatorService: modelConfig.moderator_service || modelConfig.service,
196
+ moderatorModel: modelConfig.moderator_model || modelConfig.model,
195
197
  };
196
198
  }
197
199
  logger_1.logger.warn(`Story model "${storyModel}" not found in story_models.json`);
@@ -238,9 +240,7 @@ let StoryService = class StoryService {
238
240
  room.setOnError((error) => {
239
241
  this.handleRoomError(storyId, error);
240
242
  });
241
- room.setOnSituationUpdate((situation) => {
242
- this.updateStorySituation(storyId, situation);
243
- });
243
+ room.setOnSituationUpdate((situation, latestMessage) => this.updateStorySituation(storyId, situation, latestMessage));
244
244
  room.setOnModeratorResponse((message) => {
245
245
  const portraitsToUpdate = message.changes?.update_portrait ?? [];
246
246
  if (portraitsToUpdate.length) {
@@ -249,7 +249,7 @@ let StoryService = class StoryService {
249
249
  .getAgents()
250
250
  .find((a) => a.agentNickname === nickname);
251
251
  if (characterAgent) {
252
- this.updateCharacterPortrait(room, characterAgent, storyId).catch((e) => logger_1.logger.error(`Error updating portrait for ${nickname}:`, e));
252
+ this.updateCharacterPortrait(room, characterAgent, storyId, true).catch((e) => logger_1.logger.error(`Error updating appearance for ${nickname}:`, e));
253
253
  }
254
254
  else {
255
255
  logger_1.logger.warn(`changes.update_portrait: agent "${nickname}" not found in room`);
@@ -261,13 +261,16 @@ let StoryService = class StoryService {
261
261
  if (story.situation) {
262
262
  room.setSituation(story.situation);
263
263
  }
264
+ if (story.situationTime) {
265
+ room.setSituationTime(story.situationTime);
266
+ }
264
267
  }
265
268
  return story;
266
269
  }
267
270
  async getStoryInfo(storyId) {
268
271
  return this.storyStore.getStory(storyId);
269
272
  }
270
- async createStory(name, idea, userCharacterDesc, otherCharactersDesc, illustrateStory, storyModel, isImport = false, imageModelName, imageEditModelName) {
273
+ async createStory(name, idea, userCharacterDesc, otherCharactersDesc, illustrateStory, storyModel, isImport = false, imageModelName, skipDetailedPlot = false) {
271
274
  if (!name || name.trim().length === 0) {
272
275
  throw new Error('Story name is required');
273
276
  }
@@ -290,7 +293,7 @@ let StoryService = class StoryService {
290
293
  }
291
294
  if (!aiServiceName || !aiModelName)
292
295
  throw new Error(`Failed to find model config for "${storyModel}"`);
293
- let moderator = this.agentFactory.createAgent(process.env.MODERATOR_AI_SERVICE || aiServiceName, `${storyTitle} - Moderator`, 'Moderator', 'Story moderator responsible for message routing and conversation flow', 'story_moderator', process.env.MODERATOR_AI_MODEL || aiModelName, storyAgentsDir, storyArtifactService);
296
+ let moderator = this.agentFactory.createAgent(modelConfig.moderatorService, `${storyTitle} - Moderator`, 'Moderator', 'Story moderator responsible for message routing and conversation flow', 'story_moderator', modelConfig.moderatorModel, storyAgentsDir, storyArtifactService);
294
297
  moderator.maxHistoryMessages = 16;
295
298
  moderator.reasoningEffort = 'low';
296
299
  moderator.promptCacheTTL = 60;
@@ -301,18 +304,15 @@ let StoryService = class StoryService {
301
304
  if (illustrateStory) {
302
305
  if (!imageModelName)
303
306
  imageModelName = process.env.DEFAULT_IMAGE_MODEL;
304
- if (!imageEditModelName)
305
- imageEditModelName = process.env.DEFAULT_IMAGE_EDIT_MODEL;
306
307
  }
307
308
  else {
308
309
  imageModelName = undefined;
309
- imageEditModelName = undefined;
310
310
  }
311
- const artist = this.createArtistAgent(storyInfo.id, storyAgentsDir, storyArtifactService, imageModelName);
312
- const artistEdit = this.createArtistAgent(storyInfo.id, storyAgentsDir, storyArtifactService, imageEditModelName, 'Artist (Edit)');
311
+ const artist = this.createArtistAgent(storyInfo.id, storyAgentsDir, storyArtifactService, imageModelName, 'Artist', 'generation');
312
+ const artistEdit = this.createArtistAgent(storyInfo.id, storyAgentsDir, storyArtifactService, imageModelName, 'Artist (Edit)', 'editing');
313
313
  let illustrator = null;
314
314
  if (artist) {
315
- illustrator = this.agentFactory.createAgent(process.env.MODERATOR_AI_SERVICE || aiServiceName, `${storyTitle} - Illustrator`, 'Illustrator', 'Visual artist for generating scene illustrations', 'illustrator', process.env.MODERATOR_AI_MODEL || aiModelName, storyAgentsDir, storyArtifactService);
315
+ illustrator = this.agentFactory.createAgent(modelConfig.moderatorService, `${storyTitle} - Illustrator`, 'Illustrator', 'Visual artist for generating scene illustrations', 'illustrator', modelConfig.moderatorModel, storyAgentsDir, storyArtifactService);
316
316
  illustrator.reasoningEffort = 'low';
317
317
  illustrator.maxHistoryMessages = 10;
318
318
  illustrator.promptCacheTTL = 60;
@@ -370,6 +370,7 @@ let StoryService = class StoryService {
370
370
  storyInfo.aiService = aiServiceName;
371
371
  storyInfo.aiModel = aiModelName;
372
372
  storyInfo.isImport = isImport;
373
+ storyInfo.skipDetailedPlot = skipDetailedPlot;
373
374
  this.storyStore.updateStoryMetadata(storyInfo.id, storyInfo);
374
375
  this.storyStore.saveStoryCreationParameters(storyInfo.id, idea, userCharacterDesc, otherCharactersDesc);
375
376
  const userCharacterName = userCharacterInfo.name || 'Player';
@@ -439,9 +440,7 @@ let StoryService = class StoryService {
439
440
  room.setOnError((error) => {
440
441
  this.handleRoomError(storyInfo.id, error);
441
442
  });
442
- room.setOnSituationUpdate((situation) => {
443
- this.updateStorySituation(storyInfo.id, situation);
444
- });
443
+ room.setOnSituationUpdate((situation, latestMessage) => this.updateStorySituation(storyInfo.id, situation, latestMessage));
445
444
  room.setOnModeratorResponse((message) => {
446
445
  const portraitsToUpdate = message.changes?.update_portrait ?? [];
447
446
  if (portraitsToUpdate.length) {
@@ -465,7 +464,6 @@ let StoryService = class StoryService {
465
464
  storyInfo.artistAgentId = artist?.id;
466
465
  storyInfo.artistEditAgentId = artistEdit?.id;
467
466
  storyInfo.imageModelName = imageModelName;
468
- storyInfo.imageEditModelName = imageEditModelName;
469
467
  if (userAgent) {
470
468
  storyInfo.userAgentId = userAgent.id;
471
469
  }
@@ -473,7 +471,7 @@ let StoryService = class StoryService {
473
471
  await this.updateStoryCost(storyInfo.id);
474
472
  room.ready();
475
473
  setImmediate(() => {
476
- this.createStoryAsync(storyInfo.id, name, importedStoryText, isImport).catch((error) => {
474
+ this.createStoryAsync(storyInfo.id, name, importedStoryText, isImport, skipDetailedPlot).catch((error) => {
477
475
  logger_1.logger.error('Error in async story creation:', error);
478
476
  this.progressService.emitProgress(storyInfo.id, 'error', `Error: ${error.message}`);
479
477
  this.progressService.cleanup(storyInfo.id);
@@ -492,11 +490,15 @@ let StoryService = class StoryService {
492
490
  throw error;
493
491
  }
494
492
  }
495
- async createStoryAsync(storyId, name, importedStoryText, isImport) {
493
+ async createStoryAsync(storyId, name, importedStoryText, isImport, skipDetailedPlot = false) {
496
494
  try {
497
495
  const room = await this.getStoryRoom(storyId);
498
496
  const storyInfo = this.storyStore.getStory(storyId);
499
- const plotPromptName = isImport ? 'import_plot' : 'create_plot';
497
+ const plotPromptName = isImport
498
+ ? 'import_plot'
499
+ : skipDetailedPlot
500
+ ? 'create_simple_plot'
501
+ : 'create_plot';
500
502
  const userCharPromptName = isImport ? 'import_user_character' : 'create_user_character';
501
503
  const characterPromptName = isImport ? 'import_character' : 'create_character';
502
504
  this.progressService.emitProgress(storyInfo.id, 'profiles', 'Generating detailed profiles and plot...');
@@ -549,7 +551,7 @@ let StoryService = class StoryService {
549
551
  room.publishArtifact(`Character Profile - ${userAgent.agentNickname}`, userAgent.agentNickname, response.character.character_profile, [narrator.agentNickname, userAgent.agentNickname]);
550
552
  logger_1.logger.log(`Published character profile for ${userCharacter.name} (human-controlled)`);
551
553
  if (response.character.inventory) {
552
- this.publishInventoryArtifact(room, userAgent.agentNickname, response.character.inventory);
554
+ this.storeCharacterInventory(room, userAgent.agentNickname, response.character.inventory);
553
555
  }
554
556
  this.progressService.emitProgress(storyInfo.id, 'profiles', `Finished creating character profile for ${userCharacter.name}`);
555
557
  }
@@ -588,7 +590,7 @@ let StoryService = class StoryService {
588
590
  room.publishArtifact(`Character Profile - ${agent.agentNickname}`, agent.agentNickname, response.character.character_profile, [agent.agentNickname]);
589
591
  logger_1.logger.log(`Published character profile for ${charInfo.name}`);
590
592
  if (response.character.inventory) {
591
- this.publishInventoryArtifact(room, agent.agentNickname, response.character.inventory);
593
+ this.storeCharacterInventory(room, agent.agentNickname, response.character.inventory);
592
594
  }
593
595
  this.progressService.emitProgress(storyInfo.id, 'profiles', `Finished creating character profile for ${charInfo.name}`);
594
596
  }
@@ -950,6 +952,21 @@ let StoryService = class StoryService {
950
952
  const artifactName = `Character Profile - ${characterName}`;
951
953
  this.publishArtifact(storyId, artifactName, characterName, content, []);
952
954
  }
955
+ async getCharacterInventory(storyId, characterName) {
956
+ try {
957
+ const room = await this.getStoryRoom(storyId);
958
+ const agent = room.getAgents().find((a) => a.agentNickname === characterName);
959
+ return agent?.privateContext ? agent.privateContext : null;
960
+ }
961
+ catch (error) {
962
+ logger_1.logger.error('Error getting character inventory:', error);
963
+ throw new Error(`Error getting character inventory: ${error.message}`);
964
+ }
965
+ }
966
+ async saveCharacterInventory(storyId, characterName, content) {
967
+ const room = await this.getStoryRoom(storyId);
968
+ this.storeCharacterInventory(room, characterName, content);
969
+ }
953
970
  async getPlotPlan(storyId) {
954
971
  const room = await this.getStoryRoom(storyId);
955
972
  const artifacts = room.getArtifacts();
@@ -998,7 +1015,7 @@ let StoryService = class StoryService {
998
1015
  const room = await this.getStoryRoom(storyId);
999
1016
  room.interrupt();
1000
1017
  }
1001
- async introduceCharacter(storyId, characterName, characterDescription, storyModel, fromNarratorSuggestion) {
1018
+ async introduceCharacter(storyId, characterName, characterDescription, storyModel, fromNarratorSuggestion, latestNarration) {
1002
1019
  const room = await this.getStoryRoom(storyId);
1003
1020
  if (!characterName)
1004
1021
  throw new Error('No character name provided');
@@ -1014,12 +1031,31 @@ let StoryService = class StoryService {
1014
1031
  }
1015
1032
  let aiServiceName;
1016
1033
  let aiModelName;
1017
- if (!storyModel || storyModel.trim().length === 0)
1018
- storyModel = process.env.DEFAULT_STORY_MODEL;
1019
- const modelConfig = this.getStoryModelConfig(storyModel);
1020
- if (modelConfig) {
1021
- aiServiceName = modelConfig.service;
1022
- aiModelName = modelConfig.model;
1034
+ let aiSmallModelName;
1035
+ const explicitConfig = storyModel && storyModel.trim().length > 0
1036
+ ? this.getStoryModelConfig(storyModel)
1037
+ : null;
1038
+ if (explicitConfig) {
1039
+ aiServiceName = explicitConfig.service;
1040
+ aiModelName = explicitConfig.model;
1041
+ aiSmallModelName = explicitConfig.smallModel;
1042
+ }
1043
+ else {
1044
+ const template = room.getAgents().find((a) => a.promptFolder === 'character') ??
1045
+ room.getAgents().find((a) => a.promptFolder === 'narrator');
1046
+ if (template) {
1047
+ aiServiceName = template.serviceName;
1048
+ aiModelName = template.modelName;
1049
+ aiSmallModelName = template.smallModelName;
1050
+ }
1051
+ else {
1052
+ const fallback = this.getStoryModelConfig(process.env.DEFAULT_STORY_MODEL);
1053
+ if (fallback) {
1054
+ aiServiceName = fallback.service;
1055
+ aiModelName = fallback.model;
1056
+ aiSmallModelName = fallback.smallModel;
1057
+ }
1058
+ }
1023
1059
  }
1024
1060
  if (!aiServiceName || !aiModelName) {
1025
1061
  throw new Error('Invalid story model provided');
@@ -1032,13 +1068,13 @@ let StoryService = class StoryService {
1032
1068
  const characterNickname = characterName.trim().split(/\s+/)[0];
1033
1069
  const characterAgent = this.agentFactory.createAgent(aiServiceName, `${storyTitle} - ${characterNickname}`, characterNickname, characterDescription, 'character', aiModelName, storyAgentsDir, storyArtifactService);
1034
1070
  characterAgent.promptCacheTTL = 60;
1035
- characterAgent.smallModelName = modelConfig.smallModel;
1071
+ characterAgent.smallModelName = aiSmallModelName;
1036
1072
  characterAgent.save();
1037
1073
  room.arrive(characterAgent);
1038
1074
  const introducedCharArtifacts = room.getArtifacts();
1039
1075
  const introducedCharPlotPlanArtifact = introducedCharArtifacts.find((a) => a.name === 'Plot Plan');
1040
1076
  const introducedCharPlotPlan = introducedCharPlotPlanArtifact?.body || '';
1041
- await this.updateCharacterProfiles(room, [characterAgent], introducedCharPlotPlan);
1077
+ await this.updateCharacterProfiles(room, [characterAgent], introducedCharPlotPlan, latestNarration);
1042
1078
  if (!story.characters) {
1043
1079
  story.characters = [];
1044
1080
  }
@@ -1054,14 +1090,20 @@ let StoryService = class StoryService {
1054
1090
  story.pendingSuggestion = undefined;
1055
1091
  }
1056
1092
  this.storyStore.updateStoryMetadata(storyId, story);
1057
- try {
1058
- const artifactId = await this.generateCharacterPortrait(room, characterAgent, storyId);
1059
- if (artifactId) {
1060
- logger_1.logger.log(`Generated portrait for newly introduced character: ${characterName}`);
1093
+ const portraitName = `Portrait - ${characterAgent.agentNickname}`;
1094
+ const portraitAlreadyExists = this.getStoryArtifactService(storyId)
1095
+ .listArtifacts()
1096
+ .some((a) => a.name === portraitName);
1097
+ if (!portraitAlreadyExists) {
1098
+ try {
1099
+ const artifactId = await this.generateCharacterPortrait(room, characterAgent, storyId);
1100
+ if (artifactId) {
1101
+ logger_1.logger.log(`Generated portrait for newly introduced character: ${characterName}`);
1102
+ }
1103
+ }
1104
+ catch (error) {
1105
+ logger_1.logger.error(`Error generating portrait for introduced character ${characterName}:`, error);
1061
1106
  }
1062
- }
1063
- catch (error) {
1064
- logger_1.logger.error(`Error generating portrait for introduced character ${characterName}:`, error);
1065
1107
  }
1066
1108
  if (!fromNarratorSuggestion) {
1067
1109
  room.addPendingMessage(new message_1.RoomMessage('system', app_types_1.MessageAction.MODERATE, `INTRODUCE ${characterName}`));
@@ -1367,7 +1409,8 @@ let StoryService = class StoryService {
1367
1409
  agent.save();
1368
1410
  }
1369
1411
  this.progressService.emitProgress(storyId, 'profiles', 'Updating plot plan and character profiles...');
1370
- const updatedPlotPlan = await this.updatePlotPlan(room, narrator);
1412
+ const skipDetailedPlot = this.storyStore.getStory(storyId)?.skipDetailedPlot ?? false;
1413
+ const updatedPlotPlan = await this.updatePlotPlan(room, narrator, skipDetailedPlot);
1371
1414
  await this.updateCharacterProfiles(room, characterAgents, updatedPlotPlan);
1372
1415
  await this.updateStoryCost(storyId);
1373
1416
  this.progressService.emitProgress(storyId, 'complete', 'Chapter progression complete!');
@@ -1425,14 +1468,14 @@ let StoryService = class StoryService {
1425
1468
  }
1426
1469
  return response.trim();
1427
1470
  }
1428
- async updatePlotPlan(room, narrator) {
1471
+ async updatePlotPlan(room, narrator, skipDetailedPlot = false) {
1429
1472
  const artifacts = room.getArtifacts();
1430
1473
  const plotPlanArtifact = artifacts.find((a) => a.name === 'Plot Plan');
1431
1474
  const currentPlotPlan = plotPlanArtifact ? plotPlanArtifact.body : 'No plot plan found.';
1432
1475
  const previousAllowSearch = narrator.allowSearch;
1433
1476
  narrator.allowSearch = true;
1434
1477
  try {
1435
- const progressPlotResponse = await narrator.withSmallModel(() => narrator.preparedPrompt('progress_plot', {
1478
+ const progressPlotResponse = await narrator.withSmallModel(() => narrator.preparedPrompt(skipDetailedPlot ? 'progress_simple_plot' : 'progress_plot', {
1436
1479
  currentPlotPlan: currentPlotPlan,
1437
1480
  }, false));
1438
1481
  if (progressPlotResponse) {
@@ -1452,11 +1495,17 @@ let StoryService = class StoryService {
1452
1495
  }
1453
1496
  return '';
1454
1497
  }
1455
- publishInventoryArtifact(room, characterNickname, body) {
1456
- if (!body || !body.trim())
1498
+ storeCharacterInventory(room, characterNickname, body) {
1499
+ const agent = room
1500
+ .getAgents()
1501
+ .find((a) => a.agentNickname === characterNickname);
1502
+ if (!agent) {
1503
+ logger_1.logger.warn(`storeCharacterInventory: character "${characterNickname}" not found in room`);
1457
1504
  return;
1458
- room.publishArtifact(`${characterNickname} - Inventory`, characterNickname, body, [characterNickname, 'Narrator', 'Moderator']);
1459
- logger_1.logger.log(`Published inventory for ${characterNickname}`);
1505
+ }
1506
+ agent.privateContext = body ?? '';
1507
+ agent.save();
1508
+ logger_1.logger.log(`Stored inventory for ${characterNickname}`);
1460
1509
  }
1461
1510
  applyInventoryUpdates(room, moderatorMessage) {
1462
1511
  const updates = moderatorMessage.changes?.update_inventory ?? [];
@@ -1469,53 +1518,61 @@ let StoryService = class StoryService {
1469
1518
  logger_1.logger.warn(`changes.update_inventory: skipping malformed entry: ${JSON.stringify(entry)}`);
1470
1519
  continue;
1471
1520
  }
1472
- const characterAgent = room
1473
- .getAgents()
1474
- .find((a) => a.agentNickname === characterName);
1475
- if (!characterAgent) {
1476
- logger_1.logger.warn(`changes.update_inventory: character "${characterName}" not found in room`);
1477
- continue;
1478
- }
1479
- this.publishInventoryArtifact(room, characterName, inventory);
1521
+ this.storeCharacterInventory(room, characterName, inventory);
1480
1522
  }
1481
1523
  }
1482
- async updateCharacterProfiles(room, characterAgents, plotPlan = '') {
1524
+ async updateCharacterProfiles(room, characterAgents, plotPlan = '', latestNarration) {
1483
1525
  const artifacts = room.getArtifacts();
1526
+ const characterRoster = room
1527
+ .getAgents()
1528
+ .filter((a) => a.promptFolder === 'character')
1529
+ .map((a) => ({ name: a.agentNickname, description: a.description || '' }));
1484
1530
  const updatePromises = characterAgents.map(async (character) => {
1485
1531
  try {
1486
1532
  const profileArtifact = artifacts.find((a) => a.name === `Character Profile - ${character.agentNickname}`);
1487
- let currentProfile = profileArtifact ? profileArtifact.body : null;
1533
+ const currentProfile = profileArtifact ? profileArtifact.body : null;
1488
1534
  let formattedHistory = [];
1489
1535
  const hasNativeHistory = (character.history?.length ?? 0) > 0;
1490
1536
  if (!hasNativeHistory) {
1491
1537
  formattedHistory = room
1492
1538
  .getLatestRoomMessages(0, true)
1493
1539
  .map((m) => `${m.sender}: ${m.content}\n`);
1540
+ if (latestNarration) {
1541
+ formattedHistory.push(`${room.getRoomLeader()}: ${latestNarration}\n`);
1542
+ }
1494
1543
  }
1495
- if (!currentProfile) {
1496
- currentProfile = `There is not yet a character profile for ${character.agentNickname}. Use the updated plot plan and recent story messages to create a character profile.`;
1497
- }
1498
- const progressCharacterResponse = await character.withSmallModel(() => character.preparedPrompt('progress_character', {
1544
+ const profilePromptName = currentProfile
1545
+ ? 'progress_character'
1546
+ : 'introduce_character';
1547
+ const progressCharacterResponse = await character.withSmallModel(() => character.preparedPrompt(profilePromptName, {
1499
1548
  agentName: character.agentNickname,
1500
1549
  currentProfile: currentProfile,
1501
1550
  formattedHistory: formattedHistory,
1502
1551
  plotPlan,
1552
+ otherCharacters: characterRoster.filter((c) => c.name !== character.agentNickname),
1503
1553
  }, false));
1554
+ if (progressCharacterResponse.no_changes ||
1555
+ !progressCharacterResponse.character_profile) {
1556
+ logger_1.logger.log(`No profile changes needed for ${character.agentNickname}; leaving profile unchanged`);
1557
+ return null;
1558
+ }
1504
1559
  if (progressCharacterResponse.description) {
1505
1560
  character.description = progressCharacterResponse.description;
1506
1561
  character.save();
1507
1562
  }
1508
1563
  room.publishArtifact(`Character Profile - ${character.agentNickname}`, character.agentNickname, progressCharacterResponse.character_profile, [character.agentNickname]);
1509
1564
  if (progressCharacterResponse.inventory) {
1510
- this.publishInventoryArtifact(room, character.agentNickname, progressCharacterResponse.inventory);
1565
+ this.storeCharacterInventory(room, character.agentNickname, progressCharacterResponse.inventory);
1511
1566
  }
1567
+ return character;
1512
1568
  }
1513
1569
  catch (error) {
1514
1570
  logger_1.logger.error(`Error updating character profile for ${character.agentNickname}:`, error);
1571
+ return null;
1515
1572
  }
1516
1573
  });
1517
- await Promise.all(updatePromises);
1518
- logger_1.logger.log(`Updated ${characterAgents.length} character profiles in parallel`);
1574
+ const changedAgents = (await Promise.all(updatePromises)).filter((agent) => agent !== null);
1575
+ logger_1.logger.log(`Updated ${changedAgents.length} of ${characterAgents.length} character profiles in parallel`);
1519
1576
  const pathParts = room.getStorageDir().split(path.sep);
1520
1577
  const storiesIndex = pathParts.findIndex((p) => p === 'stories');
1521
1578
  const storyId = storiesIndex >= 0 ? pathParts[storiesIndex + 1] : null;
@@ -1523,7 +1580,7 @@ let StoryService = class StoryService {
1523
1580
  logger_1.logger.error('Could not extract story ID from room path; skipping portrait updates');
1524
1581
  }
1525
1582
  else {
1526
- const portraitPromises = characterAgents.map(async (characterAgent) => {
1583
+ const portraitPromises = changedAgents.map(async (characterAgent) => {
1527
1584
  try {
1528
1585
  const artifactId = await this.updateCharacterPortrait(room, characterAgent, storyId);
1529
1586
  return {
@@ -1538,28 +1595,29 @@ let StoryService = class StoryService {
1538
1595
  }
1539
1596
  });
1540
1597
  await Promise.all(portraitPromises);
1541
- logger_1.logger.log(`Updated portraits for ${characterAgents.length} characters`);
1598
+ logger_1.logger.log(`Updated portraits for ${changedAgents.length} characters`);
1542
1599
  }
1543
1600
  }
1544
- getImageModelConfig(modelName) {
1601
+ getImageModelConfig(modelName, variant = 'generation') {
1545
1602
  try {
1546
1603
  if (!modelName) {
1547
1604
  return null;
1548
1605
  }
1549
1606
  const imageModelsPath = path.join(this.userPaths.getCommonConfigDir(), 'image_models.json');
1550
1607
  const imageModels = JSON.parse(fs.readFileSync(imageModelsPath, 'utf8'));
1551
- const modelConfig = imageModels.find((m) => m.name === modelName);
1552
- if (!modelConfig) {
1608
+ const entry = imageModels.find((m) => m.name === modelName);
1609
+ if (!entry) {
1553
1610
  logger_1.logger.warn(`Image model not found: ${modelName}`);
1554
1611
  return null;
1555
1612
  }
1556
- if (!modelConfig.service || !modelConfig.model) {
1557
- logger_1.logger.log(`Images disabled: selected "${modelName}"`);
1613
+ const variantConfig = entry[variant];
1614
+ if (!variantConfig || !variantConfig.service || !variantConfig.model) {
1615
+ logger_1.logger.log(`No ${variant} model for "${modelName}" (images disabled)`);
1558
1616
  return null;
1559
1617
  }
1560
1618
  return {
1561
- service: modelConfig.service,
1562
- model: modelConfig.model,
1619
+ service: variantConfig.service,
1620
+ model: variantConfig.model,
1563
1621
  };
1564
1622
  }
1565
1623
  catch (error) {
@@ -1571,15 +1629,22 @@ let StoryService = class StoryService {
1571
1629
  try {
1572
1630
  const imageModelsPath = path.join(this.userPaths.getCommonConfigDir(), 'image_models.json');
1573
1631
  const imageModels = JSON.parse(fs.readFileSync(imageModelsPath, 'utf8'));
1574
- const modelConfig = imageModels.find((m) => m.model === agent.modelName);
1575
- if (!modelConfig) {
1632
+ const isEditAgent = (agent.agentNickname ?? '').toLowerCase().includes('edit');
1633
+ const preferred = isEditAgent ? 'editing' : 'generation';
1634
+ const fallback = isEditAgent ? 'generation' : 'editing';
1635
+ const findVariant = (variant) => {
1636
+ const entry = imageModels.find((m) => m[variant]?.model === agent.modelName);
1637
+ return entry ? entry[variant] : null;
1638
+ };
1639
+ const variantConfig = findVariant(preferred) ?? findVariant(fallback);
1640
+ if (!variantConfig) {
1576
1641
  logger_1.logger.warn(`No image_models.json entry for "${agent.modelName}"; image generation may fail`);
1577
1642
  return {};
1578
1643
  }
1579
1644
  return {
1580
- ...(modelConfig.imageProperties ?? {}),
1581
- ...(modelConfig.centsPerImage != null
1582
- ? { centsPerImage: modelConfig.centsPerImage }
1645
+ ...(variantConfig.imageProperties ?? {}),
1646
+ ...(variantConfig.centsPerImage != null
1647
+ ? { centsPerImage: variantConfig.centsPerImage }
1583
1648
  : {}),
1584
1649
  };
1585
1650
  }
@@ -1588,9 +1653,9 @@ let StoryService = class StoryService {
1588
1653
  return {};
1589
1654
  }
1590
1655
  }
1591
- createArtistAgent(storyId, storyAgentsDir, storyArtifactService, imageModelName, nickname = 'Artist') {
1656
+ createArtistAgent(storyId, storyAgentsDir, storyArtifactService, imageModelName, nickname = 'Artist', variant = 'generation') {
1592
1657
  const storyInfo = this.storyStore.getStory(storyId);
1593
- const imageConfig = this.getImageModelConfig(imageModelName);
1658
+ const imageConfig = this.getImageModelConfig(imageModelName, variant);
1594
1659
  if (!imageConfig) {
1595
1660
  logger_1.logger.log(`Artist agent creation skipped (${nickname}): images disabled`);
1596
1661
  return null;
@@ -1651,7 +1716,7 @@ let StoryService = class StoryService {
1651
1716
  return null;
1652
1717
  }
1653
1718
  }
1654
- async updateCharacterPortrait(room, characterAgent, storyId) {
1719
+ async updateCharacterPortrait(room, characterAgent, storyId, updateDescription = false) {
1655
1720
  try {
1656
1721
  const artifacts = room.getArtifacts();
1657
1722
  const profileArtifact = artifacts.find((a) => a.name === `Character Profile - ${characterAgent.agentNickname}`);
@@ -1660,37 +1725,58 @@ let StoryService = class StoryService {
1660
1725
  const character = storyInfo.characters.find((c) => c.name === characterAgent.agentNickname);
1661
1726
  let characterDescription = profileArtifact?.body || character?.description || '';
1662
1727
  if (!characterDescription) {
1663
- logger_1.logger.warn(`No profile for ${characterAgent.agentNickname}, skipping portrait update`);
1728
+ logger_1.logger.warn(`No profile for ${characterAgent.agentNickname}, skipping appearance update`);
1664
1729
  return null;
1665
1730
  }
1666
1731
  const storyArtifactService = this.getStoryArtifactService(storyId);
1667
- if (!storyInfo.artistEditAgentId) {
1668
- logger_1.logger.warn(`No edit artist configured for story ${storyId}, skipping portrait update`);
1669
- return null;
1732
+ const artistEditAgent = storyInfo.artistEditAgentId
1733
+ ? await this.agentStore.getAgent(storyInfo.artistEditAgentId)
1734
+ : null;
1735
+ const hasArtist = !!artistEditAgent;
1736
+ if (artistEditAgent) {
1737
+ artistEditAgent.artifactService = storyArtifactService;
1670
1738
  }
1671
- const artistEditAgent = await this.agentStore.getAgent(storyInfo.artistEditAgentId);
1672
- if (!artistEditAgent) {
1673
- logger_1.logger.warn(`Edit artist agent not found: ${storyInfo.artistEditAgentId}`);
1739
+ if (!updateDescription && !hasArtist) {
1740
+ logger_1.logger.warn(`No edit artist configured for story ${storyId}, skipping portrait update`);
1674
1741
  return null;
1675
1742
  }
1676
- artistEditAgent.artifactService = storyArtifactService;
1677
1743
  const portraitName = `Portrait - ${characterAgent.agentNickname}`;
1678
- const existingPortrait = storyArtifactService
1679
- .listArtifacts()
1680
- .find((a) => a.name === portraitName);
1681
- if (!existingPortrait) {
1744
+ const existingPortrait = hasArtist
1745
+ ? storyArtifactService.listArtifacts().find((a) => a.name === portraitName)
1746
+ : undefined;
1747
+ if (!updateDescription && hasArtist && !existingPortrait) {
1682
1748
  logger_1.logger.warn(`No existing portrait for ${characterAgent.agentNickname}, generating from scratch`);
1683
1749
  return this.generateCharacterPortrait(room, characterAgent, storyId);
1684
1750
  }
1685
- const visualDescription = await characterAgent.preparedPrompt('update_portrait', {
1751
+ const portraitResponse = await characterAgent.preparedPrompt('update_portrait', {
1686
1752
  characterName: characterAgent.agentNickname,
1687
1753
  characterProfile: characterDescription,
1754
+ currentDescription: characterAgent.description || '',
1688
1755
  scenario: scenario,
1689
1756
  }, false);
1757
+ if (updateDescription &&
1758
+ portraitResponse?.description &&
1759
+ typeof portraitResponse.description === 'string') {
1760
+ characterAgent.description = portraitResponse.description;
1761
+ characterAgent.save();
1762
+ if (character) {
1763
+ character.description = portraitResponse.description;
1764
+ this.storyStore.updateStoryMetadata(storyId, storyInfo);
1765
+ }
1766
+ logger_1.logger.log(`Updated appearance description for ${characterAgent.agentNickname}`);
1767
+ }
1768
+ if (!hasArtist) {
1769
+ return null;
1770
+ }
1771
+ const visualDescription = portraitResponse?.visual_prompt;
1690
1772
  if (!visualDescription || typeof visualDescription !== 'string') {
1691
1773
  logger_1.logger.warn(`Failed to generate update description for ${characterAgent.agentNickname}`);
1692
1774
  return null;
1693
1775
  }
1776
+ if (!existingPortrait) {
1777
+ logger_1.logger.warn(`No existing portrait for ${characterAgent.agentNickname}, generating from scratch`);
1778
+ return this.generateCharacterPortrait(room, characterAgent, storyId);
1779
+ }
1694
1780
  const imageResult = await artistEditAgent.editImage(visualDescription, this.getImagePropertiesForAgent(artistEditAgent), [existingPortrait.artifactId]);
1695
1781
  if (imageResult.error) {
1696
1782
  return null;
@@ -1708,7 +1794,7 @@ let StoryService = class StoryService {
1708
1794
  return null;
1709
1795
  }
1710
1796
  }
1711
- async generateSceneIllustration(storyId, situation, allowReference = true) {
1797
+ async generateSceneIllustration(storyId, situation, allowReference = true, latestNarration) {
1712
1798
  try {
1713
1799
  const story = this.storyStore.getStory(storyId);
1714
1800
  if (!story?.illustratorAgentId) {
@@ -1719,13 +1805,17 @@ let StoryService = class StoryService {
1719
1805
  const room = await this.roomStore.getRoom(story.roomId);
1720
1806
  if (!illustrator || !room)
1721
1807
  return;
1722
- const allMessages = room.getLatestRoomMessages(0, true);
1723
- const latestMessage = allMessages[allMessages.length - 1];
1724
- if (!latestMessage) {
1725
- logger_1.logger.warn('No messages in room');
1726
- return;
1808
+ let narrationText = latestNarration;
1809
+ if (!narrationText) {
1810
+ const allMessages = room.getLatestRoomMessages(0, true);
1811
+ const latestMessage = allMessages[allMessages.length - 1];
1812
+ if (!latestMessage) {
1813
+ logger_1.logger.warn('No messages in room');
1814
+ return;
1815
+ }
1816
+ narrationText = latestMessage.content;
1727
1817
  }
1728
- illustrator.addToConversationHistory(latestMessage.content, false);
1818
+ illustrator.addToConversationHistory(narrationText, false);
1729
1819
  const storyArtifactService = this.getStoryArtifactService(storyId);
1730
1820
  const allArtifacts = storyArtifactService.listArtifacts();
1731
1821
  const existingScenes = allArtifacts
@@ -1746,7 +1836,8 @@ let StoryService = class StoryService {
1746
1836
  if (!existingScenesString)
1747
1837
  existingScenesString =
1748
1838
  'No scene reference images have been created yet. (set isReference: true)';
1749
- let prompt = `**Current Situation**:\n ${story.situation}\n\n**Existing Scenes**:\n ${existingScenesString}`;
1839
+ const situationForScene = `${story.situationTime ? `${story.situationTime} - ` : ''}${story.situation}`;
1840
+ let prompt = `**Current Situation**:\n ${situationForScene}\n\n**Existing Scenes**:\n ${existingScenesString}`;
1750
1841
  const sceneAnalysis = await illustrator.instructedPrompt(prompt, 'describe_scene', {
1751
1842
  characters: portraitCharacters,
1752
1843
  }, true, true);
@@ -1762,7 +1853,7 @@ let StoryService = class StoryService {
1762
1853
  }
1763
1854
  const referenceCreated = await this.generateSceneReference(storyId, sceneAnalysis.sceneName, sceneAnalysis.sceneDescription);
1764
1855
  if (referenceCreated) {
1765
- await this.generateSceneIllustration(storyId, situation, false);
1856
+ await this.generateSceneIllustration(storyId, situation, false, latestNarration);
1766
1857
  }
1767
1858
  }
1768
1859
  else {
@@ -1933,21 +2024,16 @@ let StoryService = class StoryService {
1933
2024
  }
1934
2025
  return result;
1935
2026
  }
1936
- formatDisplaySituation(parsed, fallback) {
1937
- if (parsed.time && parsed.situation)
1938
- return `${parsed.time} - ${parsed.situation}`;
1939
- if (parsed.situation)
1940
- return parsed.situation;
1941
- return fallback;
1942
- }
1943
- async updateStorySituation(storyId, preamble) {
2027
+ async updateStorySituation(storyId, preamble, latestNarration) {
1944
2028
  const story = this.storyStore.getStory(storyId);
1945
2029
  if (!story)
1946
2030
  return;
1947
2031
  const room = await this.getStoryRoom(storyId);
1948
2032
  const parsed = this.parseNarratorPreamble(preamble);
1949
- const displaySituation = this.formatDisplaySituation(parsed, preamble);
1950
- room.setSituation(displaySituation);
2033
+ const situationText = parsed.situation ?? preamble;
2034
+ const situationTime = parsed.time ?? '';
2035
+ room.setSituation(situationText);
2036
+ room.setSituationTime(situationTime);
1951
2037
  if (parsed.progressChapter) {
1952
2038
  story.declinedCharacters = [];
1953
2039
  story.pendingSuggestion = {
@@ -1972,19 +2058,20 @@ let StoryService = class StoryService {
1972
2058
  logger_1.logger.log(`Character ${newCharacterName} was declined, skipping prompt`);
1973
2059
  return;
1974
2060
  }
1975
- story.pendingSuggestion = {
1976
- type: 'suggest-introduce-character',
1977
- characterName: newCharacterName,
1978
- timestamp: Date.now(),
1979
- };
1980
- this.storyStore.updateStoryMetadata(storyId, story);
1981
- this.messageService.emitIntroduceCharacter(storyId, newCharacterName);
2061
+ logger_1.logger.log(`Auto-creating narrator-introduced character ${newCharacterName}`);
2062
+ try {
2063
+ await this.introduceCharacter(storyId, newCharacterName, '', undefined, true, latestNarration);
2064
+ }
2065
+ catch (error) {
2066
+ logger_1.logger.error(`Error auto-creating character ${newCharacterName} for story ${storyId}:`, error);
2067
+ }
1982
2068
  return;
1983
2069
  }
1984
- story.situation = displaySituation;
2070
+ story.situation = situationText;
2071
+ story.situationTime = situationTime;
1985
2072
  this.storyStore.updateStoryMetadata(storyId, story);
1986
- logger_1.logger.log(`Updated story ${storyId} situation: ${displaySituation}`);
1987
- this.generateSceneIllustration(storyId, displaySituation).catch((error) => {
2073
+ logger_1.logger.log(`Updated story ${storyId} situation: ${situationTime ? `${situationTime} - ` : ''}${situationText}`);
2074
+ this.generateSceneIllustration(storyId, situationText, true, latestNarration).catch((error) => {
1988
2075
  logger_1.logger.error('Illustration generation error:', error);
1989
2076
  });
1990
2077
  }