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
@@ -5,9 +5,9 @@ const app_types_1 = require("../../../shared/types/app.types");
5
5
  const message_1 = require("./message");
6
6
  const crypto_1 = require("crypto");
7
7
  const errors_1 = require("../common/errors");
8
- const child_process_1 = require("child_process");
9
8
  const logger_1 = require("../common/logger");
10
9
  class Room {
10
+ static MAX_TOOL_ITERATIONS = 5;
11
11
  id;
12
12
  name;
13
13
  goal;
@@ -28,11 +28,14 @@ class Room {
28
28
  onStateChangeCallbacks = new Map();
29
29
  onError;
30
30
  currentSituation = '';
31
+ currentSituationTime = '';
31
32
  onSituationUpdate;
32
33
  onModeratorResponseCallback;
33
34
  templateRoomId;
34
35
  storageDir;
35
36
  roomStoreService;
37
+ toolStoreService;
38
+ toolExecutionService;
36
39
  constructor(id, name, goal, baseRoomDir = '', storageDir = '', roomStoreService = null) {
37
40
  this.name = name;
38
41
  this.goal = goal;
@@ -147,7 +150,7 @@ class Room {
147
150
  getLatestRoomMessages(fromIndex = 0, includeThoughts = false) {
148
151
  const messages = this.roomMessages.filter((msg) => msg.index >= fromIndex);
149
152
  if (!includeThoughts) {
150
- return messages.filter((msg) => msg.action !== app_types_1.MessageAction.THINK);
153
+ return messages.filter((msg) => msg.action !== app_types_1.MessageAction.THINK && msg.action !== app_types_1.MessageAction.TOOL);
151
154
  }
152
155
  return messages;
153
156
  }
@@ -158,12 +161,12 @@ class Room {
158
161
  });
159
162
  this.nextMessageIndex = this.roomMessages.length;
160
163
  }
161
- addRoomMessage(message) {
164
+ addRoomMessage(message, options) {
162
165
  message.index = this.nextMessageIndex++;
163
166
  this.roomMessages.push(message);
164
167
  this.saveMessages([message]);
165
- if (message.situation) {
166
- this.handleSituationUpdate(message.situation);
168
+ if (message.situation && !options?.suppressSituationUpdate) {
169
+ void this.handleSituationUpdate(message.situation);
167
170
  }
168
171
  this.notifyStateChange();
169
172
  }
@@ -233,6 +236,12 @@ class Room {
233
236
  }
234
237
  }
235
238
  }
239
+ if (normalizedAction === app_types_1.MessageAction.EXECUTE && (!response.name || !response.name.trim())) {
240
+ needsModeration = true;
241
+ }
242
+ if (normalizedAction === app_types_1.MessageAction.COMMISSION && targets.length === 0) {
243
+ needsModeration = true;
244
+ }
236
245
  }
237
246
  if (needsModeration) {
238
247
  return new message_1.RoomMessage(agent.agentNickname, app_types_1.MessageAction.MODERATE, content, [], null, situation);
@@ -249,6 +258,10 @@ class Room {
249
258
  let i = 0;
250
259
  while (i < lines.length && lines[i].trim() === '')
251
260
  i++;
261
+ const fenceRe = /^\s*```\w*\s*$/;
262
+ const hadLeadingFence = i < lines.length && fenceRe.test(lines[i]);
263
+ if (hadLeadingFence)
264
+ i++;
252
265
  const tagLineRe = /^\s*<\w+>[\s\S]*?<\/\w+>\s*$/;
253
266
  if (i >= lines.length || !tagLineRe.test(lines[i]))
254
267
  return null;
@@ -261,7 +274,16 @@ class Room {
261
274
  const preamble = preambleLines.join('\n');
262
275
  if (j < lines.length && lines[j].trim() === '')
263
276
  j++;
264
- const content = lines.slice(j).join('\n').trimStart();
277
+ let contentLines = lines.slice(j);
278
+ if (hadLeadingFence) {
279
+ let k = contentLines.length - 1;
280
+ while (k >= 0 && contentLines[k].trim() === '')
281
+ k--;
282
+ if (k >= 0 && fenceRe.test(contentLines[k])) {
283
+ contentLines = contentLines.slice(0, k);
284
+ }
285
+ }
286
+ const content = contentLines.join('\n').trimStart();
265
287
  return { preamble, content };
266
288
  }
267
289
  static coerceMessageAction(raw) {
@@ -271,45 +293,162 @@ class Room {
271
293
  const values = Object.values(app_types_1.MessageAction);
272
294
  return values.includes(lowered) ? lowered : undefined;
273
295
  }
274
- async getAgentAiResponse(agent, onResponse) {
275
- let answer = null;
276
- try {
277
- const visibleArtifacts = this.getVisibleArtifacts(agent.agentNickname)
278
- .map((a) => `${a.name} (by ${a.creator}): ${a.body}`)
279
- .join('\n\n');
280
- const allArtifacts = this.getArtifacts();
281
- const invisibleArtifactNames = allArtifacts
282
- .filter((a) => !this.getVisibleArtifacts(agent.agentNickname).includes(a))
283
- .map((a) => a.name)
284
- .join(', ');
285
- const humanAgent = this.getAgents().find((a) => a.isControlledByHuman);
286
- const humanCharacterName = humanAgent ? humanAgent.agentNickname : 'Player';
287
- const maxOutputChars = 4096;
288
- const variables = {
289
- agentName: agent.agentName,
290
- agentNickname: agent.agentNickname,
291
- agentDescription: agent.description,
292
- otherAgents: this.agents.filter((a) => a.agentNickname !== agent.agentNickname),
293
- roomLeader: this.getRoomLeader(),
294
- roomGoal: this.getGoal(),
295
- scenario: this.getGoal(),
296
- situation: this.getSituation() || 'Story beginning',
297
- visibleArtifacts: visibleArtifacts,
298
- invisibleArtifactNames: invisibleArtifactNames,
299
- humanCharacterName: humanCharacterName,
300
- humanAgentName: humanCharacterName,
301
- maxOutputChars: maxOutputChars
302
- };
303
- let prompt = null;
304
- let situation = this.getSituation();
305
- if (situation)
306
- prompt = `Current Situation: ${situation}`;
307
- let response = await agent.instructedPrompt(prompt, 'perceive', variables, false, true, maxOutputChars);
308
- answer = this.parseAgentResponse(agent, response);
309
- agent.addToConversationHistory(response, true);
296
+ getPresentCharacters(perceivingAgent, message) {
297
+ const audienceNames = message.getTargets();
298
+ const senderName = message.getSender();
299
+ return this.agents.filter((a) => {
300
+ if (a.promptFolder !== 'character')
301
+ return false;
302
+ if (a.agentNickname === perceivingAgent.agentNickname)
303
+ return false;
304
+ if (this.checkAgentNicknameMatch(a.agentNickname, senderName))
305
+ return true;
306
+ if (audienceNames.some((name) => this.checkAgentNicknameMatch(a.agentNickname, name)))
307
+ return true;
308
+ if (audienceNames.length === 0 && message.canHear(a.agentNickname))
309
+ return true;
310
+ return false;
311
+ });
312
+ }
313
+ buildCharacterPrivateContextDigest() {
314
+ return this.agents
315
+ .filter((a) => a.promptFolder === 'character' && a.privateContext && a.privateContext.trim())
316
+ .map((a) => `### ${a.agentNickname}\n${a.privateContext}`)
317
+ .join('\n\n');
318
+ }
319
+ async buildAvailableTools(agent) {
320
+ if (!this.toolStoreService || !agent.toolNames || agent.toolNames.length === 0) {
321
+ return [];
322
+ }
323
+ const tools = [];
324
+ for (const name of agent.toolNames) {
325
+ const tool = await this.toolStoreService.getTool(name);
326
+ if (tool) {
327
+ tools.push({
328
+ name: tool.name,
329
+ description: tool.description,
330
+ parameters: tool.parameters || [],
331
+ });
332
+ }
333
+ }
334
+ return tools;
335
+ }
336
+ async buildPerceiveVariables(agent) {
337
+ const visibleArtifacts = this.getVisibleArtifacts(agent.agentNickname)
338
+ .map((a) => `${a.name} (by ${a.creator}): ${a.body}`)
339
+ .join('\n\n');
340
+ const allArtifacts = this.getArtifacts();
341
+ const invisibleArtifactNames = allArtifacts
342
+ .filter((a) => !this.getVisibleArtifacts(agent.agentNickname).includes(a))
343
+ .map((a) => a.name)
344
+ .join(', ');
345
+ const humanAgent = this.getAgents().find((a) => a.isControlledByHuman);
346
+ const humanCharacterName = humanAgent ? humanAgent.agentNickname : 'Player';
347
+ const maxOutputChars = 4096;
348
+ return {
349
+ agentName: agent.agentName,
350
+ agentNickname: agent.agentNickname,
351
+ agentDescription: agent.description,
352
+ otherAgents: this.agents.filter((a) => a.agentNickname !== agent.agentNickname),
353
+ roomLeader: this.getRoomLeader(),
354
+ roomGoal: this.getGoal(),
355
+ scenario: this.getGoal(),
356
+ situation: this.getSituation() || 'Story beginning',
357
+ visibleArtifacts: visibleArtifacts,
358
+ invisibleArtifactNames: invisibleArtifactNames,
359
+ humanCharacterName: humanCharacterName,
360
+ humanAgentName: humanCharacterName,
361
+ maxOutputChars: maxOutputChars,
362
+ availableTools: await this.buildAvailableTools(agent),
363
+ };
364
+ }
365
+ buildTransientPrompt(agent, message) {
366
+ let prompt = this.buildSituationHeader() || null;
367
+ if (message) {
368
+ const presentCharacters = this.getPresentCharacters(agent, message);
369
+ if (presentCharacters.length > 0) {
370
+ const descriptions = presentCharacters
371
+ .map((c) => `- ${c.agentNickname}: ${c.description}`)
372
+ .join('\n');
373
+ const charactersPresent = `Characters present (how they currently appear to you):\n${descriptions}`;
374
+ prompt = prompt ? `${prompt}\n\n${charactersPresent}` : charactersPresent;
375
+ }
376
+ }
377
+ if (agent.privateContext && agent.privateContext.trim()) {
378
+ const privateBlock = `Your private context (known only to you):\n${agent.privateContext}`;
379
+ prompt = prompt ? `${prompt}\n\n${privateBlock}` : privateBlock;
380
+ }
381
+ if (agent.agentNickname === this.getRoomLeader()) {
382
+ const digest = this.buildCharacterPrivateContextDigest();
383
+ if (digest) {
384
+ const digestBlock = `Character Inventories and Conditions:\n${digest}`;
385
+ prompt = prompt ? `${prompt}\n\n${digestBlock}` : digestBlock;
386
+ }
387
+ }
388
+ return prompt;
389
+ }
390
+ async promptAgentOnce(agent, transientPrompt, variables) {
391
+ const response = await agent.instructedPrompt(transientPrompt, 'perceive', variables, false, true, variables.maxOutputChars);
392
+ const answer = this.parseAgentResponse(agent, response);
393
+ agent.addToConversationHistory(response, true);
394
+ agent.save();
395
+ return answer;
396
+ }
397
+ async runAgentTurn(agent, heardMessage, opts = {}) {
398
+ const variables = await this.buildPerceiveVariables(agent);
399
+ let response = await this.promptAgentOnce(agent, this.buildTransientPrompt(agent, heardMessage), variables);
400
+ for (let i = 0; i < Room.MAX_TOOL_ITERATIONS; i++) {
401
+ if (this.isPaused || this.isAdjourned)
402
+ return null;
403
+ let feedback;
404
+ if (response.action === app_types_1.MessageAction.PUBLISH && response.name) {
405
+ this.publishArtifact(response.name, agent.agentNickname, response.content, response.targets);
406
+ const wasDeleted = !response.content || !response.content.trim();
407
+ feedback = `From system: Artifact "${response.name}" was ${wasDeleted ? 'deleted' : 'published'}.`;
408
+ }
409
+ else if (response.action === app_types_1.MessageAction.EXECUTE) {
410
+ feedback = await this.executeToolForAgent(agent, response);
411
+ }
412
+ else if (opts.inCommission && response.action === app_types_1.MessageAction.COMMISSION) {
413
+ feedback = `From system: Nested commissions are not permitted. Complete your own task and respond with action "deliver".`;
414
+ }
415
+ else {
416
+ if (!opts.inCommission && response.action === app_types_1.MessageAction.DELIVER) {
417
+ response.action = app_types_1.MessageAction.SPEAK;
418
+ }
419
+ return response;
420
+ }
421
+ agent.addToConversationHistory(feedback, false);
310
422
  agent.save();
423
+ response = await this.promptAgentOnce(agent, this.buildTransientPrompt(agent, null), variables);
311
424
  }
312
- catch (e) {
425
+ if (this.isPaused || this.isAdjourned)
426
+ return null;
427
+ agent.addToConversationHistory(`From system: You have reached the tool-call limit for this turn. Respond now with an audible action (speak, whisper, think, ...).`, false);
428
+ agent.save();
429
+ response = await this.promptAgentOnce(agent, this.buildTransientPrompt(agent, null), variables);
430
+ if (response.action === app_types_1.MessageAction.EXECUTE ||
431
+ response.action === app_types_1.MessageAction.PUBLISH) {
432
+ response = new message_1.RoomMessage(agent.agentNickname, app_types_1.MessageAction.THINK, response.content, []);
433
+ }
434
+ return response;
435
+ }
436
+ async executeToolForAgent(agent, message) {
437
+ const toolName = message.name;
438
+ const result = this.toolExecutionService
439
+ ? await this.toolExecutionService.runForAgent(toolName, message.content, agent.toolNames || [])
440
+ : { ok: false, error: 'Tool execution is not available in this room' };
441
+ const argsPreview = message.content && message.content.length > 200
442
+ ? `${message.content.slice(0, 200)}...`
443
+ : message.content || '';
444
+ const statusText = 'error' in result ? `error: ${result.error}` : 'ok';
445
+ this.addRoomMessage(new message_1.RoomMessage(agent.agentNickname, app_types_1.MessageAction.TOOL, `${toolName}(${argsPreview}) → ${statusText}`));
446
+ return `From system: Result of tool "${toolName}":\n${JSON.stringify(result)}`;
447
+ }
448
+ getAgentAiResponse(agent, message, onResponse) {
449
+ this.runAgentTurn(agent, message)
450
+ .then((answer) => onResponse(answer))
451
+ .catch((e) => {
313
452
  if (e instanceof errors_1.InsufficientBalanceError) {
314
453
  this.handleInsufficientBalance(e);
315
454
  }
@@ -317,8 +456,8 @@ class Room {
317
456
  let internalizedError = `${agent.agentNickname} thinks: No one heard me because ${e.message}`;
318
457
  agent.addToConversationHistory(internalizedError, true);
319
458
  }
320
- }
321
- onResponse(answer);
459
+ onResponse(null);
460
+ });
322
461
  }
323
462
  perceive(agent, message, onResponse) {
324
463
  if (!message.canHear(agent.agentNickname)) {
@@ -334,7 +473,7 @@ class Room {
334
473
  if (agent.isControlledByHuman) {
335
474
  return;
336
475
  }
337
- this.getAgentAiResponse(agent, onResponse);
476
+ this.getAgentAiResponse(agent, message, onResponse);
338
477
  }
339
478
  async moderate(moderatorAgent, message, onResponse) {
340
479
  const humanAgent = this.getAgents().find((agent) => agent.isControlledByHuman);
@@ -361,10 +500,14 @@ class Room {
361
500
  invisibleArtifactNames: invisibleArtifactNames,
362
501
  };
363
502
  let prompt = `
364
- Current Situation: ${this.getSituation()}
503
+ ${this.buildSituationHeader()}
365
504
  Sender: ${message.getSender()}
366
505
  Recent Message: ${message.getContent()}
367
506
  `;
507
+ const characterContexts = this.buildCharacterPrivateContextDigest();
508
+ if (characterContexts) {
509
+ prompt += `\nCharacter Inventories and Conditions:\n${characterContexts}\n`;
510
+ }
368
511
  try {
369
512
  let response = await moderatorAgent.instructedPrompt(prompt, 'moderate', variables, false, true);
370
513
  let answer = this.parseAgentResponse(moderatorAgent, response);
@@ -458,6 +601,10 @@ Recent Message: ${message.getContent()}
458
601
  setRoomStoreService(service) {
459
602
  this.roomStoreService = service;
460
603
  }
604
+ setToolServices(toolStore, toolExecution) {
605
+ this.toolStoreService = toolStore;
606
+ this.toolExecutionService = toolExecution;
607
+ }
461
608
  saveMessages(messages) {
462
609
  if (!this.roomStoreService || !this.storageDir || !this.id) {
463
610
  return;
@@ -507,6 +654,22 @@ Recent Message: ${message.getContent()}
507
654
  getSituation() {
508
655
  return this.currentSituation;
509
656
  }
657
+ getSituationTime() {
658
+ return this.currentSituationTime;
659
+ }
660
+ setSituationTime(time) {
661
+ this.currentSituationTime = time || '';
662
+ }
663
+ buildSituationHeader() {
664
+ const lines = [];
665
+ if (this.currentSituationTime) {
666
+ lines.push(`Current time: ${this.currentSituationTime} (24-hour clock)`);
667
+ }
668
+ if (this.currentSituation) {
669
+ lines.push(`Current Situation: ${this.currentSituation}`);
670
+ }
671
+ return lines.join('\n');
672
+ }
510
673
  setSituation(situation) {
511
674
  this.currentSituation = situation;
512
675
  }
@@ -516,11 +679,11 @@ Recent Message: ${message.getContent()}
516
679
  setOnModeratorResponse(callback) {
517
680
  this.onModeratorResponseCallback = callback;
518
681
  }
519
- handleSituationUpdate(situation) {
682
+ async handleSituationUpdate(situation, messageBody) {
520
683
  logger_1.logger.log(`Situation updated: ${situation}`);
521
684
  this.currentSituation = situation;
522
685
  if (this.onSituationUpdate) {
523
- this.onSituationUpdate(situation);
686
+ await this.onSituationUpdate(situation, messageBody);
524
687
  }
525
688
  }
526
689
  handlePublishMessages(messages) {
@@ -534,37 +697,6 @@ Recent Message: ${message.getContent()}
534
697
  }
535
698
  }
536
699
  }
537
- handleExecuteMessages(messages) {
538
- for (const message of messages) {
539
- if (message.content && message.content.trim()) {
540
- const command = message.content.trim();
541
- const creator = message.sender;
542
- const description = message.name || 'Command execution';
543
- const resultViewers = message.targets;
544
- this.executeCommand(command, creator, description, resultViewers);
545
- }
546
- }
547
- }
548
- executeCommand(command, creator, description, resultViewers = []) {
549
- const workingDir = this.baseRoomDir || process.cwd();
550
- logger_1.logger.log(`FEATURE DISABLED -- Executing command: ${command} in directory: ${workingDir} by agent: ${creator}`);
551
- return;
552
- (0, child_process_1.exec)(command, { cwd: workingDir }, (error, stdout, stderr) => {
553
- let result = '';
554
- if (error) {
555
- result = `Error: ${error.message}\n${stderr}`;
556
- logger_1.logger.error(`Command execution error: ${error.message}`);
557
- }
558
- else if (stderr) {
559
- result = `${stdout}\n\nStderr: ${stderr}`;
560
- }
561
- else {
562
- result = stdout;
563
- }
564
- const artifactBody = `Command: ${command}\nWorking Directory: ${workingDir}\n\n${result}`;
565
- this.publishArtifact(description, creator, artifactBody, resultViewers);
566
- });
567
- }
568
700
  handleAdjournMessages(messages) {
569
701
  if (messages.length === 0) {
570
702
  return;
@@ -680,7 +812,7 @@ Recent Message: ${message.getContent()}
680
812
  }
681
813
  logger_1.logger.log(`Moderator routing: sender=${originalMessage.sender} action=${moderatorMessage.action}, name=${responderNickname}, targets=${targets.join(', ')}`);
682
814
  if (moderatorMessage.action === app_types_1.MessageAction.RELAY) {
683
- this.addRoomMessage(new message_1.RoomMessage(originalMessage.sender, originalMessage.action, content, originalMessage.targets, originalMessage.name, originalMessage.situation));
815
+ this.addRoomMessage(new message_1.RoomMessage(originalMessage.sender, originalMessage.action, content, originalMessage.targets, originalMessage.name, originalMessage.situation), { suppressSituationUpdate: true });
684
816
  }
685
817
  for (const recipientName of targets) {
686
818
  if (recipientName === responderNickname)
@@ -711,6 +843,7 @@ Recent Message: ${message.getContent()}
711
843
  return;
712
844
  }
713
845
  this.setCurrentSpeaker(responderNickname);
846
+ originalMessage.targets = targets;
714
847
  this.perceive(responderAgent, originalMessage, (response) => {
715
848
  if (this.isPaused) {
716
849
  this.processingInProgress = false;
@@ -738,6 +871,149 @@ Recent Message: ${message.getContent()}
738
871
  }
739
872
  }
740
873
  }
874
+ async runDeliverTurn(agent, commission) {
875
+ try {
876
+ let response = await this.runAgentTurn(agent, commission, { inCommission: true });
877
+ if (response === null)
878
+ return null;
879
+ if (response.action !== app_types_1.MessageAction.DELIVER) {
880
+ agent.addToConversationHistory(`From system: You were commissioned by ${commission.sender}. Respond now with action "deliver" targeting ${commission.sender}, containing your complete result.`, false);
881
+ agent.save();
882
+ const variables = await this.buildPerceiveVariables(agent);
883
+ response = await this.promptAgentOnce(agent, this.buildTransientPrompt(agent, null), variables);
884
+ }
885
+ if (response.action !== app_types_1.MessageAction.DELIVER) {
886
+ return new message_1.RoomMessage(agent.agentNickname, app_types_1.MessageAction.DELIVER, response.content, [commission.sender]);
887
+ }
888
+ response.sender = agent.agentNickname;
889
+ response.targets = [commission.sender];
890
+ return response;
891
+ }
892
+ catch (e) {
893
+ if (e instanceof errors_1.InsufficientBalanceError) {
894
+ this.handleInsufficientBalance(e);
895
+ return null;
896
+ }
897
+ logger_1.logger.error(`Commissioned agent ${agent.agentNickname} failed:`, e);
898
+ return new message_1.RoomMessage(agent.agentNickname, app_types_1.MessageAction.DELIVER, `(No deliverable: ${agent.agentNickname} encountered an error: ${e.message})`, [commission.sender]);
899
+ }
900
+ }
901
+ async handleCommissionMessage(msg) {
902
+ try {
903
+ const aiTargets = [];
904
+ const humanTargetNames = [];
905
+ const unknownNames = [];
906
+ for (const name of [...new Set(msg.getTargets())]) {
907
+ const agent = this.agents.find((a) => this.checkAgentNicknameMatch(a.agentNickname, name));
908
+ if (!agent) {
909
+ unknownNames.push(name);
910
+ }
911
+ else if (agent.agentNickname === msg.sender) {
912
+ }
913
+ else if (agent.isControlledByHuman) {
914
+ humanTargetNames.push(agent.agentNickname);
915
+ }
916
+ else if (!aiTargets.includes(agent)) {
917
+ aiTargets.push(agent);
918
+ }
919
+ }
920
+ for (const agent of this.agents) {
921
+ if (agent.agentNickname === msg.sender)
922
+ continue;
923
+ agent.addToConversationHistory(msg, false);
924
+ agent.save();
925
+ }
926
+ const commissioner = this.agents.find((a) => this.checkAgentNicknameMatch(a.agentNickname, msg.sender));
927
+ if (aiTargets.length === 0) {
928
+ const reasons = [];
929
+ if (unknownNames.length > 0)
930
+ reasons.push(`unknown agents: ${unknownNames.join(', ')}`);
931
+ if (humanTargetNames.length > 0)
932
+ reasons.push(`human-controlled agents cannot be commissioned: ${humanTargetNames.join(', ')}`);
933
+ const note = `From system: Your commission could not be delivered (${reasons.join('; ') || 'no valid targets'}).`;
934
+ await this.respondToCommissioner(commissioner, note);
935
+ return;
936
+ }
937
+ const settled = await Promise.allSettled(aiTargets.map((t) => this.runDeliverTurn(t, msg)));
938
+ if (this.isPaused || this.isAdjourned || !this.processingInProgress) {
939
+ if (this.processingInProgress) {
940
+ this.processingInProgress = false;
941
+ this.notifyStateChange();
942
+ }
943
+ return;
944
+ }
945
+ const delivers = settled.map((result, i) => {
946
+ if (result.status === 'fulfilled' && result.value)
947
+ return result.value;
948
+ return new message_1.RoomMessage(aiTargets[i].agentNickname, app_types_1.MessageAction.DELIVER, `(No deliverable: ${aiTargets[i].agentNickname} did not complete the commission)`, [msg.sender]);
949
+ });
950
+ for (const deliver of delivers) {
951
+ this.addRoomMessage(deliver);
952
+ for (const agent of this.agents) {
953
+ if (agent.agentNickname === deliver.sender)
954
+ continue;
955
+ if (this.checkAgentNicknameMatch(agent.agentNickname, msg.sender))
956
+ continue;
957
+ agent.addToConversationHistory(deliver, false);
958
+ agent.save();
959
+ }
960
+ }
961
+ const sections = delivers.map((d) => `## From ${d.sender}\n${d.content}`);
962
+ if (humanTargetNames.length > 0) {
963
+ sections.push(`(Skipped: ${humanTargetNames.join(', ')} — human-controlled agents cannot be commissioned)`);
964
+ }
965
+ if (unknownNames.length > 0) {
966
+ sections.push(`(Unknown agents: ${unknownNames.join(', ')})`);
967
+ }
968
+ const aggregate = `From system: Deliverables for your commission:\n\n${sections.join('\n\n')}`;
969
+ await this.respondToCommissioner(commissioner, aggregate);
970
+ }
971
+ catch (error) {
972
+ logger_1.logger.error('Error handling commission message:', error);
973
+ this.processingInProgress = false;
974
+ this.notifyStateChange();
975
+ if (this.pendingMessages && this.pendingMessages.length > 0) {
976
+ this.processPendingMessages();
977
+ }
978
+ }
979
+ }
980
+ async respondToCommissioner(commissioner, note) {
981
+ if (!commissioner) {
982
+ logger_1.logger.warn('Commissioner not found in room; dropping commission results');
983
+ }
984
+ else {
985
+ commissioner.addToConversationHistory(note, false);
986
+ commissioner.save();
987
+ if (commissioner.isControlledByHuman) {
988
+ this.addRoomMessage(new message_1.RoomMessage('system', app_types_1.MessageAction.SYSTEM, note, [
989
+ commissioner.agentNickname,
990
+ ]));
991
+ this.processingInProgress = false;
992
+ this.notifyStateChange();
993
+ this.setCurrentSpeaker(commissioner.agentNickname);
994
+ return;
995
+ }
996
+ let reply = null;
997
+ try {
998
+ reply = await this.runAgentTurn(commissioner, null);
999
+ }
1000
+ catch (e) {
1001
+ if (e instanceof errors_1.InsufficientBalanceError) {
1002
+ this.handleInsufficientBalance(e);
1003
+ return;
1004
+ }
1005
+ throw e;
1006
+ }
1007
+ if (reply !== null && !this.isPaused && !this.isAdjourned) {
1008
+ this.pendingMessages = [...(this.pendingMessages || []), reply];
1009
+ }
1010
+ }
1011
+ this.processingInProgress = false;
1012
+ this.notifyStateChange();
1013
+ if (this.pendingMessages && this.pendingMessages.length > 0) {
1014
+ this.processPendingMessages();
1015
+ }
1016
+ }
741
1017
  processPendingMessages() {
742
1018
  if (this.isPaused ||
743
1019
  this.processingInProgress ||
@@ -750,7 +1026,10 @@ Recent Message: ${message.getContent()}
750
1026
  const messages = [...this.pendingMessages];
751
1027
  this.pendingMessages = [];
752
1028
  this.handlePublishMessages(messages.filter((m) => m.getAction() === app_types_1.MessageAction.PUBLISH));
753
- this.handleExecuteMessages(messages.filter((m) => m.getAction() === app_types_1.MessageAction.EXECUTE));
1029
+ const strayExecuteMessages = messages.filter((m) => m.getAction() === app_types_1.MessageAction.EXECUTE);
1030
+ if (strayExecuteMessages.length > 0) {
1031
+ logger_1.logger.warn(`Dropping ${strayExecuteMessages.length} queued EXECUTE message(s); tool calls are handled within the sender's turn`);
1032
+ }
754
1033
  this.handleAdjournMessages(messages.filter((m) => m.getAction() === app_types_1.MessageAction.ADJOURN));
755
1034
  if (this.isAdjourned) {
756
1035
  logger_1.logger.log(`Room ${this.name} has been adjourned. Message processing stopped.`);
@@ -776,21 +1055,34 @@ Recent Message: ${message.getContent()}
776
1055
  if (moderateMessages.length > 1) {
777
1056
  this.pendingMessages = [...moderateMessages.slice(1), ...this.pendingMessages];
778
1057
  }
779
- this.moderate(moderator, originalMessage, (response) => {
780
- if (this.isPaused) {
781
- this.processingInProgress = false;
782
- this.notifyStateChange();
783
- return;
1058
+ const dispatchToModerator = async () => {
1059
+ if (originalMessage.situation) {
1060
+ await this.handleSituationUpdate(originalMessage.situation, originalMessage.getContent());
784
1061
  }
785
- if (response !== null) {
786
- this.handleModeratorResponse(response, originalMessage);
787
- }
788
- else {
789
- this.processingInProgress = false;
790
- this.notifyStateChange();
791
- if (this.pendingMessages && this.pendingMessages.length > 0) {
792
- this.processPendingMessages();
1062
+ await this.moderate(moderator, originalMessage, (response) => {
1063
+ if (this.isPaused) {
1064
+ this.processingInProgress = false;
1065
+ this.notifyStateChange();
1066
+ return;
1067
+ }
1068
+ if (response !== null) {
1069
+ this.handleModeratorResponse(response, originalMessage);
793
1070
  }
1071
+ else {
1072
+ this.processingInProgress = false;
1073
+ this.notifyStateChange();
1074
+ if (this.pendingMessages && this.pendingMessages.length > 0) {
1075
+ this.processPendingMessages();
1076
+ }
1077
+ }
1078
+ });
1079
+ };
1080
+ dispatchToModerator().catch((error) => {
1081
+ logger_1.logger.error('Error dispatching message to moderator:', error);
1082
+ this.processingInProgress = false;
1083
+ this.notifyStateChange();
1084
+ if (this.pendingMessages && this.pendingMessages.length > 0) {
1085
+ this.processPendingMessages();
794
1086
  }
795
1087
  });
796
1088
  return;
@@ -812,6 +1104,10 @@ Recent Message: ${message.getContent()}
812
1104
  nextMessage = filteredMessage;
813
1105
  }
814
1106
  this.addRoomMessage(nextMessage);
1107
+ if (nextMessage.getAction() === app_types_1.MessageAction.COMMISSION) {
1108
+ void this.handleCommissionMessage(nextMessage);
1109
+ return;
1110
+ }
815
1111
  this.setCurrentSpeaker(nextMessage.targets[0]);
816
1112
  let agentsInProgress = this.agents.length;
817
1113
  if (agentsInProgress === 0) {
@@ -859,7 +1155,8 @@ Recent Message: ${message.getContent()}
859
1155
  filtered = shoutMessages;
860
1156
  }
861
1157
  if (filtered.length > 1) {
862
- const speakMessages = filtered.filter((m) => m.getAction() === app_types_1.MessageAction.SPEAK);
1158
+ const speakMessages = filtered.filter((m) => m.getAction() === app_types_1.MessageAction.SPEAK ||
1159
+ m.getAction() === app_types_1.MessageAction.COMMISSION);
863
1160
  const whisperMessages = filtered.filter((m) => m.getAction() === app_types_1.MessageAction.WHISPER);
864
1161
  if (speakMessages.length > 0) {
865
1162
  filtered = speakMessages;
@@ -1,7 +1,7 @@
1
1
  export { Briyah } from './briyah';
2
2
  export { BriyahConfigService, BriyahConfigOptions } from './briyah-config';
3
3
  export type { AppService } from '../app.service';
4
- export type { AgentInfo, CreateAgentResponse, RoomInfo, RoomDetails, CreateRoomResponse, StoryInfo, CreateStoryResponse, ProcessTextResponse, FileList, PromptFileContent, AttachDocumentResponse, AgentMessagesResponse, RoomMessagesResponse, StoryState, Message, MessageAction, ModelInfo, ArtifactMetadata, PreparedPromptResponse, PromptScope, PromptFolder, PromptFoldersResponse, PromptFile, PromptFilesResponse, StoryIdea, Transaction, TransactionHistoryResponse, StoryStateEvent, StoryIntroduceCharacterEvent, StoryProgressChapterEvent, StoryErrorEvent, Character, ChapterInfo, } from '../../../shared/types/app.types';
4
+ export type { AgentInfo, CreateAgentResponse, RoomInfo, RoomDetails, CreateRoomResponse, StoryInfo, CreateStoryResponse, ProcessTextResponse, FileList, PromptFileContent, AttachDocumentResponse, AgentMessagesResponse, RoomMessagesResponse, StoryState, Message, MessageAction, ModelInfo, ArtifactMetadata, PreparedPromptResponse, PromptScope, PromptFolder, PromptFoldersResponse, PromptFile, PromptFilesResponse, StoryIdea, Transaction, TransactionHistoryResponse, ToolDefinition, ToolParameter, ToolsResponse, ToolRunResult, StoryStateEvent, StoryIntroduceCharacterEvent, StoryProgressChapterEvent, StoryErrorEvent, Character, ChapterInfo, } from '../../../shared/types/app.types';
5
5
  export type { LoggingOptions } from './briyah-config';
6
6
  export { Agent } from '../ai/agent';
7
7
  export { Room } from '../room/room';