briyah 1.1.8 → 1.2.1

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 (116) hide show
  1. package/data/common/config/markup +1 -1
  2. package/data/common/config/model_prices.json +3775 -653
  3. package/data/common/prompts/character/create_character.json +33 -29
  4. package/data/common/prompts/character/create_character.prompt +13 -2
  5. package/data/common/prompts/character/create_user_character.json +32 -28
  6. package/data/common/prompts/character/create_user_character.prompt +13 -2
  7. package/data/common/prompts/character/progress_character.json +22 -18
  8. package/data/common/prompts/character/progress_character.prompt +6 -2
  9. package/data/common/prompts/illustrator/describe_character.prompt +31 -0
  10. package/data/common/prompts/illustrator/describe_scene.json +19 -1
  11. package/data/common/prompts/illustrator/describe_scene.prompt +2 -1
  12. package/data/common/prompts/narrator/perceive.prompt +96 -35
  13. package/data/common/prompts/story_moderator/moderate.json +33 -7
  14. package/data/common/prompts/story_moderator/moderate.prompt +166 -20
  15. package/dist-sdk/server/src/ai/LLM/anthropic.service.d.ts +5 -2
  16. package/dist-sdk/server/src/ai/LLM/anthropic.service.js +27 -15
  17. package/dist-sdk/server/src/ai/LLM/base-ai.service.d.ts +6 -2
  18. package/dist-sdk/server/src/ai/LLM/base-ai.service.js +20 -10
  19. package/dist-sdk/server/src/ai/LLM/deepseek.service.js +10 -0
  20. package/dist-sdk/server/src/ai/LLM/googleai.service.js +11 -8
  21. package/dist-sdk/server/src/ai/LLM/grok.service.js +8 -5
  22. package/dist-sdk/server/src/ai/LLM/openai.service.js +12 -11
  23. package/dist-sdk/server/src/ai/LLM/together.service.js +9 -6
  24. package/dist-sdk/server/src/ai/agent-factory.d.ts +5 -3
  25. package/dist-sdk/server/src/ai/agent-factory.js +6 -4
  26. package/dist-sdk/server/src/ai/agent-store.service.d.ts +5 -3
  27. package/dist-sdk/server/src/ai/agent-store.service.js +15 -15
  28. package/dist-sdk/server/src/ai/agent.d.ts +9 -6
  29. package/dist-sdk/server/src/ai/agent.js +22 -6
  30. package/dist-sdk/server/src/ai/ai-debug-logger.d.ts +10 -0
  31. package/dist-sdk/server/src/ai/ai-debug-logger.js +82 -0
  32. package/dist-sdk/server/src/ai/artifact.module.js +2 -2
  33. package/dist-sdk/server/src/ai/artifact.service.d.ts +3 -3
  34. package/dist-sdk/server/src/ai/artifact.service.js +6 -6
  35. package/dist-sdk/server/src/ai/attached-file.module.js +2 -2
  36. package/dist-sdk/server/src/ai/attached-file.service.d.ts +3 -3
  37. package/dist-sdk/server/src/ai/attached-file.service.js +6 -6
  38. package/dist-sdk/server/src/app/balance.service.d.ts +3 -3
  39. package/dist-sdk/server/src/app/balance.service.js +7 -7
  40. package/dist-sdk/server/src/app/transaction.service.d.ts +3 -3
  41. package/dist-sdk/server/src/app/transaction.service.js +4 -4
  42. package/dist-sdk/server/src/app/user-service-factory.d.ts +3 -1
  43. package/dist-sdk/server/src/app/user-service-factory.js +18 -14
  44. package/dist-sdk/server/src/app/user-service-manager.d.ts +1 -3
  45. package/dist-sdk/server/src/app.service.d.ts +3 -3
  46. package/dist-sdk/server/src/app.service.js +30 -30
  47. package/dist-sdk/server/src/config/env-config.d.ts +3 -0
  48. package/dist-sdk/server/src/config/env-config.js +2 -0
  49. package/dist-sdk/server/src/config/user-paths.module.d.ts +2 -0
  50. package/dist-sdk/server/src/config/{configuration.module.js → user-paths.module.js} +8 -8
  51. package/dist-sdk/server/src/config/{configuration.service.d.ts → user-paths.service.d.ts} +1 -1
  52. package/dist-sdk/server/src/config/{configuration.service.js → user-paths.service.js} +3 -3
  53. package/dist-sdk/server/src/room/message.d.ts +1 -1
  54. package/dist-sdk/server/src/room/message.js +1 -1
  55. package/dist-sdk/server/src/room/room-store.module.js +2 -2
  56. package/dist-sdk/server/src/room/room-store.service.d.ts +3 -3
  57. package/dist-sdk/server/src/room/room-store.service.js +6 -6
  58. package/dist-sdk/server/src/room/room.d.ts +1 -0
  59. package/dist-sdk/server/src/room/room.js +43 -19
  60. package/dist-sdk/server/src/sdk/briyah-config.d.ts +2 -1
  61. package/dist-sdk/server/src/sdk/briyah-config.js +4 -0
  62. package/dist-sdk/server/src/sdk/briyah.js +1 -1
  63. package/dist-sdk/server/src/story/story-store.service.d.ts +3 -3
  64. package/dist-sdk/server/src/story/story-store.service.js +6 -6
  65. package/dist-sdk/server/src/story/story.service.d.ts +10 -3
  66. package/dist-sdk/server/src/story/story.service.js +233 -99
  67. package/dist-sdk/shared/types/app.types.d.ts +1 -0
  68. package/docs/assets/hierarchy.js +1 -1
  69. package/docs/assets/search.js +1 -1
  70. package/docs/classes/Agent.html +16 -16
  71. package/docs/classes/Briyah.html +12 -12
  72. package/docs/classes/BriyahConfigService.html +5 -5
  73. package/docs/classes/Room.html +24 -24
  74. package/docs/classes/RoomMessage.html +11 -11
  75. package/docs/enums/MessageAction.html +3 -3
  76. package/docs/hierarchy.html +1 -1
  77. package/docs/index.html +2 -2
  78. package/docs/interfaces/AgentInfo.html +2 -2
  79. package/docs/interfaces/AgentMessagesResponse.html +2 -2
  80. package/docs/interfaces/AppService.html +154 -154
  81. package/docs/interfaces/Artifact.html +3 -3
  82. package/docs/interfaces/ArtifactMetadata.html +2 -2
  83. package/docs/interfaces/AttachDocumentResponse.html +2 -2
  84. package/docs/interfaces/BriyahConfigOptions.html +7 -7
  85. package/docs/interfaces/ChapterInfo.html +2 -2
  86. package/docs/interfaces/Character.html +2 -2
  87. package/docs/interfaces/CreateAgentResponse.html +2 -2
  88. package/docs/interfaces/CreateRoomResponse.html +2 -2
  89. package/docs/interfaces/CreateStoryResponse.html +2 -2
  90. package/docs/interfaces/FileList.html +2 -2
  91. package/docs/interfaces/LoggingOptions.html +10 -6
  92. package/docs/interfaces/Message.html +2 -2
  93. package/docs/interfaces/ModelInfo.html +2 -2
  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 +3 -3
  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/Transaction.html +2 -2
  112. package/docs/interfaces/TransactionHistoryResponse.html +2 -2
  113. package/docs/modules.html +1 -1
  114. package/docs/types/PromptScope.html +1 -1
  115. package/package.json +2 -1
  116. package/dist-sdk/server/src/config/configuration.module.d.ts +0 -2
@@ -21,6 +21,13 @@ Filter inappropriate language and content from messages.
21
21
  ### Human Character Name
22
22
  {{humanAgentName}}
23
23
 
24
+ ### Character Inventories and Conditions
25
+ You have access to each character's `[CharacterName] - Inventory` artifact (visible only to you, the Narrator, and the character themselves). These artifacts list what each character is currently carrying, wearing, or possessing, plus any notable physical or mental conditions (wounded, exhausted, drunk, transformed, etc.). Use these to keep the story self-consistent — when a character refers to "my sword" the inventory should support it, when a character takes damage you should record the wound, etc.
26
+
27
+ {{#if visibleArtifacts}}
28
+ {{visibleArtifacts}}
29
+ {{/if}}
30
+
24
31
 
25
32
  ## Your Task
26
33
  Based upon the provided context, determine:
@@ -32,6 +39,13 @@ To accomplish your task:
32
39
  - Analyze the Current Situation in the context of the Story Scenario and your (limited) message history
33
40
  - Determine whether the Sender of the recent message is trying to 1) perform an action or 2) only participate in dialog
34
41
 
42
+ ### The Current Situation is authoritative
43
+ The **Current Situation** line is the single source of truth about which characters are physically present in the active scene, and where. When routing:
44
+ - Only characters named in the Current Situation are "present". Other characters are elsewhere and cannot hear or respond unless the Current Situation itself changes.
45
+ - Do **not** infer that a character is present from vague references in the body of a narration (e.g., "the men outside", "the others", "everyone"). If a character is not named in the Current Situation, treat them as absent — pick a different responder.
46
+ - Do **not** assume a scene shift based on body content alone. A scene shift only happens when the Current Situation itself names a different time, place, or set of characters. If the situation still says "Jessica and Mòr in the kitchen", the responder must be one of Jessica, Mòr, or the Narrator — never a character who is miles away in another storyline.
47
+ - If the narration genuinely describes a character who is not in the Current Situation (e.g., a brief aside about someone elsewhere), keep the responder among the characters who ARE in the Current Situation; do not hand the turn off to the absent character.
48
+
35
49
  ### If the Sender is intending to perform an action:
36
50
  Route the message to the Narrator.
37
51
  The Narrator will interpret the message and narrate the outcome of the action.
@@ -53,11 +67,26 @@ This request will *not* become part of the readable story, only the narrator's r
53
67
  3. Responder: The one character you choose to respond next to best maintain the flow of the conversation and further the story.
54
68
  4. Targets: List of all characters who would realistically be able to hear the message (even if they shouldn't respond).
55
69
  - When processing a message from the Narrator, the targets array should only include characters who are present for the situation being narrated.
56
- 5. Update Portrait: List the names of any characters whose visible appearance has just changed in a way that should be reflected in their portrait image.
57
- - Include a character's name when the current message describes a meaningful change to their visible appearance: new or different clothing/outfit, significant injury (wounds, blood, bandages), physical transformation, or a notable item they are now visibly carrying or wearing.
58
- - Use the character's exact name as it appears in the Available Characters list.
59
- - Leave the array empty if no visible appearance changes occurred.
60
- - Minor or invisible changes (emotional state alone, unseen actions) do NOT warrant a portrait update.
70
+ 5. Changes: An object containing any story state changes triggered by this message.
71
+ - `update_portrait`: list the names of any characters whose visible appearance has just changed in a way that should be reflected in their portrait image.
72
+ - Include a character's name when the current message describes a meaningful change to their visible appearance: new or different clothing/outfit, significant injury (wounds, blood, bandages), physical transformation, or a notable item they are now visibly carrying or wearing.
73
+ - Use the character's exact name as it appears in the Available Characters list.
74
+ - Leave the array empty if no visible appearance changes occurred.
75
+ - Minor or invisible changes (emotional state alone, unseen actions) do NOT warrant a portrait update.
76
+ - `update_inventory`: list of character inventory artifacts that should be rewritten in light of this message. Each entry replaces the named character's `[Name] - Inventory` artifact in full.
77
+ - Include an entry when items are gained, lost, consumed, broken, given, taken, equipped, unequipped, OR when a notable physical/mental trait or condition changes (wounded, healed, exhausted, intoxicated, transformed, cursed, blessed, etc.).
78
+ - The `inventory` field must contain the COMPLETE new inventory body (not a diff). Read the character's current inventory artifact from the context above, apply the change, and return the updated artifact text.
79
+ - Use this structure (omit a section if it would be empty):
80
+ ```
81
+ ## Items
82
+ - <item 1>
83
+ - <item 2>
84
+
85
+ ## Conditions
86
+ - <condition 1>
87
+ ```
88
+ - Keep it concise — names of items, not flavor prose. Conditions should be short phrases ("wounded right arm", "exhausted").
89
+ - Leave the array empty if no inventory or condition changes occurred.
61
90
 
62
91
  ## Content Moderation
63
92
  - Do *not* allow profanity or swear words like 'hell', 'shit', and 'damn' or 'damned'.
@@ -73,7 +102,10 @@ You must respond with a valid JSON object in the following format:
73
102
  "content": "relay", // or modified message body
74
103
  "name": "CharacterName1", // Name of character who should respond next
75
104
  "targets": ["CharacterName1", "CharacterName2"], // List of all characters who would realistically be able to hear the message
76
- "update_portrait": [] // Names of characters whose visible appearance changed; empty if none
105
+ "changes": {
106
+ "update_portrait": [], // Names of characters whose visible appearance changed; empty if none
107
+ "update_inventory": [] // Inventory/condition updates; empty if none
108
+ }
77
109
  }
78
110
 
79
111
  ## Conversation Rules
@@ -86,6 +118,18 @@ You must respond with a valid JSON object in the following format:
86
118
  - When a character sends "PASS", route to Narrator with PASS command (relay_silent), or to another contextually appropriate character if the situation calls for it.
87
119
  - **Important**:Avoid lengthy conversations that exclude the Human Character completely. Give the Human Character a chance to interact with the story.
88
120
 
121
+ ### When no character is available to respond — auto-PASS to the Narrator
122
+ Sometimes no character in the Current Situation can realistically be the next speaker. The most common case is when the only present character is asleep, unconscious, dead, paralyzed, in a coma, or otherwise narratively unable to speak or act (check their `[Name] - Inventory` artifact's `## Conditions` section). It can also happen when the Current Situation is a transitional beat with no living character actively present.
123
+
124
+ In these cases, emit an automatic PASS on the human's behalf, exactly as you would if the Human Character had typed "PASS":
125
+ - Set `name` to `"Narrator"` — the Narrator can always continue the story.
126
+ - Keep `action` as `"relay"` so the original narration is still logged to the readable story.
127
+ - Keep `targets` as the characters who are present in the scene (even if all of them are asleep — they can still "hear" the narration in a passive sense).
128
+
129
+ The Narrator can always respond and is the safe fallback. Do this whether the unavailable character is the Human Character or an AI character — the rule is about who can speak in the *story*, not about who is at the keyboard.
130
+
131
+ Note: "Resting", "tired", "lightly intoxicated", "wounded but conscious" are NOT disqualifying — those characters can still respond. The bar is whether they could realistically speak or act in the next beat of the story.
132
+
89
133
  ## Introductions
90
134
  - Only set "name" to the exact character name (nickname) of one of the characters in the **Available Characters** list.
91
135
  - If a message from the Human Character (or any character) directly addresses someone who is NOT listed in the **Available Characters** list, route the message to the Narrator. The Narrator has access to the secret plot plan and can identify whether the addressed person is a planned character; do NOT guess at a name yourself.
@@ -112,7 +156,7 @@ Correct Response:
112
156
  "content": "PASS",
113
157
  "name": "Narrator",
114
158
  "targets": ["Narrator"],
115
- "update_portrait": []
159
+ "changes": { "update_portrait": [] }
116
160
  }
117
161
  - Mike is choosing not to act or speak, so select another appropriate character (or the narrator) to speak next instead.
118
162
  - Use "relay_silent" because PASS is a meta-action and shouldn't appear in the story
@@ -126,7 +170,7 @@ Correct Response:
126
170
  "content": "relay",
127
171
  "name": "Mike",
128
172
  "targets": ["Mike", "Bob"],
129
- "update_portrait": []
173
+ "changes": { "update_portrait": [] }
130
174
  }
131
175
  - According to the current situation, only Mike, Jessica, and Bob are within hearing distance of each other.
132
176
  - Jessica is speaking, and only Mike and Bob can hear. Sergei and Carter are not present.
@@ -143,7 +187,7 @@ Correct Response:
143
187
  "content": "relay",
144
188
  "name": "Jessica",
145
189
  "targets": ["Jessica", "Bob"],
146
- "update_portrait": []
190
+ "changes": { "update_portrait": [] }
147
191
  }
148
192
  - According to the current situation, only Mike, Jessica, and Bob are within hearing distance of each other.
149
193
  - Mike is speaking, and only Jessica and Bob can hear. Sergei and Carter are not present.
@@ -160,7 +204,7 @@ Correct Response:
160
204
  "content": "relay",
161
205
  "name": "Narrator",
162
206
  "targets": ["Narrator"],
163
- "update_portrait": []
207
+ "changes": { "update_portrait": [] }
164
208
  }
165
209
  - The sender is attempting to perform an action with the message
166
210
  - Only the Narrator is able to handle the character's request to perform an action.
@@ -178,7 +222,7 @@ Correct Response:
178
222
  "content": "relay",
179
223
  "name": "Mike",
180
224
  "targets": ["Mike", "Jessica", "Bob"],
181
- "update_portrait": []
225
+ "changes": { "update_portrait": [] }
182
226
  }
183
227
  - This is not dialog and there is no obvious responder, so choose the human character to respond because they are involved in the current situation.
184
228
  - All characters involved in the current situation should be targets of the message
@@ -194,13 +238,32 @@ Correct Response:
194
238
  "content": "relay",
195
239
  "name": "Sergei",
196
240
  "targets": ["Sergei", "Carter"],
197
- "update_portrait": []
241
+ "changes": { "update_portrait": [] }
198
242
  }
199
243
  - This is not dialog and there is no obvious responder, so choose an character involved in the current situation to be the responder
200
244
  - All characters involved in the current situation should be targets of the message
201
245
  - Mike, Jessica, and Bob should not be targets of this message because they are not involved in the situation.
202
246
 
203
247
 
248
+ ### Example 5b: Narration mentions an absent character, but the Current Situation has NOT shifted
249
+ Current Situation: 17:35 - Mike and Jessica are in the kitchen as the evening meal is being prepared
250
+ Recent Message Sender: Narrator
251
+ Recent Message: The kitchen smells of stew and woodsmoke. Outside, the men are tending to the horses. Somewhere else, Sergei is at his cottage that morning, washing at the basin and turning over what he saw the night before.
252
+ Correct Response:
253
+ {
254
+ "action": "relay",
255
+ "content": "relay",
256
+ "name": "Mike",
257
+ "targets": ["Mike", "Jessica"],
258
+ "changes": { "update_portrait": [] }
259
+ }
260
+ - The Current Situation places Mike and Jessica in the kitchen — that is authoritative. Sergei is NOT in the Current Situation, even though the narrator mentions him in passing.
261
+ - Do not let the brief reference to Sergei convince you the scene has shifted. A scene shift requires the Current Situation itself to name new characters or a new place.
262
+ - Do not let the phrase "the men" make you pick a named character — "the men" is a vague reference, not a routing instruction.
263
+ - The responder must be a character present in the Current Situation. Mike (the Human Character) is present, so route to Mike.
264
+ - Targets are only the characters in the Current Situation; do not include Sergei or any absent characters.
265
+
266
+
204
267
  ### Example 6: Character addresses someone not listed in the Available Characters list
205
268
  Recent Message Sender: Mike (human character)
206
269
  Recent Message: Hello, Albert Flanagan!
@@ -210,7 +273,7 @@ Correct Response:
210
273
  "content": "relay",
211
274
  "name": "Narrator",
212
275
  "targets": ["Narrator"],
213
- "update_portrait": []
276
+ "changes": { "update_portrait": [] }
214
277
  }
215
278
  - "Albert Flanagan" is not in the Available Characters list.
216
279
  - Route to the Narrator. The Narrator can consult the plot plan to identify the addressed character (and emit an appropriate INTRODUCE situation) or narrate an alternative response.
@@ -225,7 +288,7 @@ Correct Response:
225
288
  "content": "[message content modified to remove explicit content]",
226
289
  "name": "Narrator",
227
290
  "targets": ["Narrator"],
228
- "update_portrait": []
291
+ "changes": { "update_portrait": [] }
229
292
  }
230
293
  - If the message contains explicit content, just remove the explicit content if possible or modify the message to something more appropriate.
231
294
 
@@ -238,37 +301,120 @@ Correct Response:
238
301
  "content": "relay",
239
302
  "name": "",
240
303
  "targets": [],
241
- "update_portrait": []
304
+ "changes": { "update_portrait": [] }
242
305
  }
243
306
  - Use the relay_silent action and leave the name field blank and set the targets list to an empty array.
244
307
  - The message will be silently relayed to the Narrator to perform the introduction.
245
308
 
246
309
 
247
- ### Example 9: Narrator describes a visible appearance change
310
+ ### Example 9: Narrator describes a visible appearance change (and a wound)
248
311
  Recent Message Sender: Narrator
249
312
  Recent Message: The arrow grazes Jessica's arm, tearing her sleeve and leaving a bloody gash. She wraps a strip of cloth around the wound and keeps moving.
313
+ Suppose Jessica's existing inventory is:
314
+ ## Items
315
+ - Hunting bow
316
+ - Quiver with 7 arrows
317
+ - Hunting knife
250
318
  Correct Response:
251
319
  {
252
320
  "action": "relay",
253
321
  "content": "relay",
254
322
  "name": "Mike",
255
323
  "targets": ["Mike", "Jessica", "Bob"],
256
- "update_portrait": ["Jessica"]
324
+ "changes": {
325
+ "update_portrait": ["Jessica"],
326
+ "update_inventory": [
327
+ {
328
+ "characterName": "Jessica",
329
+ "inventory": "## Items\n- Hunting bow\n- Quiver with 7 arrows\n- Hunting knife\n\n## Conditions\n- Wounded right arm (bandaged)"
330
+ }
331
+ ]
332
+ }
257
333
  }
258
334
  - Jessica's visible appearance has changed (torn sleeve, wound, makeshift bandage), so her portrait should be updated.
335
+ - A wound is a condition worth tracking in her inventory artifact. Carry forward her existing items unchanged, and add the new condition.
259
336
  - All characters present in the current situation are targets.
260
337
 
261
338
 
262
- ### Example 10: Character action with no visible change
339
+ ### Example 10: Character action with no visible change (item gained)
263
340
  Recent Message Sender: Bob
264
341
  Recent Message: Bob wields his new sword proudly above his head
342
+ Suppose Bob's existing inventory is:
343
+ ## Items
344
+ - Bedroll
345
+ - Tinderbox
265
346
  Correct Response:
266
347
  {
267
348
  "action": "relay_silent",
268
349
  "content": "relay",
269
350
  "name": "Narrator",
270
351
  "targets": ["Narrator"],
271
- "update_portrait": ["Bob"]
352
+ "changes": {
353
+ "update_portrait": ["Bob"],
354
+ "update_inventory": [
355
+ {
356
+ "characterName": "Bob",
357
+ "inventory": "## Items\n- Bedroll\n- Tinderbox\n- Iron sword"
358
+ }
359
+ ]
360
+ }
361
+ }
362
+ - Bob just got a new weapon, so it should appear in his portrait AND be added to his inventory items.
363
+ - The inventory field carries the FULL updated artifact body, not just the new item.
364
+
365
+
366
+ ### Example 11: Pure inventory change with no visual update
367
+ Recent Message Sender: Narrator
368
+ Recent Message: Mike takes a long pull from the whiskey flask. The warmth spreads through his chest as the world softens at the edges.
369
+ Suppose Mike's existing inventory is:
370
+ ## Items
371
+ - Whiskey flask (3/4 full)
372
+ - Wool blanket
373
+ Correct Response:
374
+ {
375
+ "action": "relay",
376
+ "content": "relay",
377
+ "name": "Mike",
378
+ "targets": ["Mike", "Jessica", "Bob"],
379
+ "changes": {
380
+ "update_portrait": [],
381
+ "update_inventory": [
382
+ {
383
+ "characterName": "Mike",
384
+ "inventory": "## Items\n- Whiskey flask (1/2 full)\n- Wool blanket\n\n## Conditions\n- Lightly intoxicated"
385
+ }
386
+ ]
387
+ }
388
+ }
389
+ - Mike consumed some whiskey, so the flask amount decreased and he is now mildly drunk.
390
+ - No visible appearance change, so update_portrait is empty.
391
+
392
+
393
+ ### Example 12: No character available to respond — auto-PASS to Narrator
394
+ Current Situation: 23:50 - Jessica is alone in the small side room at Balmore, having just settled in for the night
395
+ Suppose Jessica's inventory is:
396
+ ## Items
397
+ - Wool blanket
398
+ - Water avens root (wrapped in cloth)
399
+
400
+ ## Conditions
401
+ - Asleep
402
+ Recent Message Sender: Narrator
403
+ Recent Message: The wind softens to a hush against the shutter. Jessica's breathing slows and evens. The candle gutters once, then steadies, throwing long shadows on the wattle wall.
404
+ Correct Response:
405
+ {
406
+ "action": "relay",
407
+ "content": "relay",
408
+ "name": "Narrator",
409
+ "targets": ["Jessica"],
410
+ "changes": {
411
+ "update_portrait": [],
412
+ "update_inventory": []
413
+ }
272
414
  }
273
- - Bob just got a new weapon (or other visible item) that we would want to include in his portrait.
415
+ - Jessica is the only character in the scene, but her conditions say **Asleep** she cannot speak or act in the story right now. There is no one to respond.
416
+ - Auto-PASS to the **Narrator** by setting `name` to "Narrator". The Narrator can always continue the story — transition to the next scene, advance time, cut to another character, etc.
417
+ - Keep `action: "relay"` so the narrator's atmospheric narration is still logged to the readable story.
418
+ - Jessica stays in `targets` because she's physically present in the scene (and could be woken later), but she is NOT the responder `name`.
419
+ - "Asleep" is already in her conditions; no inventory update needed.
274
420
 
@@ -1,7 +1,8 @@
1
1
  import { Anthropic } from '@anthropic-ai/sdk';
2
2
  import { Agent } from '../agent';
3
3
  import { BaseAiService } from './base-ai.service';
4
- import { ConfigurationService } from '../../config/configuration.service';
4
+ import { UserPathsService } from '../../config/user-paths.service';
5
+ import { IConfigService } from '../../config/env-config';
5
6
  import { AgentStoreService } from '../agent-store.service';
6
7
  import { BalanceService } from '../../app/balance.service';
7
8
  import { ArtifactService } from '../artifact.service';
@@ -9,7 +10,7 @@ import { ModelInfo } from '../../../../shared/types/app.types';
9
10
  export declare class AnthropicAiService extends BaseAiService {
10
11
  protected anthropic: Anthropic;
11
12
  constructor();
12
- createAgent(configService: ConfigurationService, agentStoreService: AgentStoreService, balanceService: BalanceService, artifactService: ArtifactService, agentName: string, agentNickname: string, description: string, promptFolder: string, modelName: string, storageDir?: string): Agent;
13
+ createAgent(userPaths: UserPathsService, configService: IConfigService, agentStoreService: AgentStoreService, balanceService: BalanceService, artifactService: ArtifactService, agentName: string, agentNickname: string, description: string, promptFolder: string, modelName: string, storageDir?: string): Agent;
13
14
  getServiceName(): string;
14
15
  supportsImageGeneration(): boolean;
15
16
  supportsPromptCaching(): boolean;
@@ -23,5 +24,7 @@ export declare class AnthropicAiService extends BaseAiService {
23
24
  fetchModelsFromApi(): Promise<ModelInfo[]>;
24
25
  addToConversationHistory(agent: Agent, message: string, fromSelf?: boolean): void;
25
26
  textPrompt(agent: Agent, prompt: string, jsonSchema?: any, saveResponse?: boolean, promptInstructions?: string, cacheConfig?: any, maxOutputChars?: number): Promise<any>;
27
+ private static readonly EFFORT_CONFIG_MODEL_VERSIONS;
28
+ private usesEffortConfig;
26
29
  private computeMessageCost;
27
30
  }
@@ -8,6 +8,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
8
8
  var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
+ var AnthropicAiService_1;
11
12
  Object.defineProperty(exports, "__esModule", { value: true });
12
13
  exports.AnthropicAiService = void 0;
13
14
  const common_1 = require("@nestjs/common");
@@ -17,6 +18,7 @@ const model_prices_1 = require("../model_prices");
17
18
  const errors_1 = require("../../common/errors");
18
19
  const logger_1 = require("../../common/logger");
19
20
  let AnthropicAiService = class AnthropicAiService extends base_ai_service_1.BaseAiService {
21
+ static { AnthropicAiService_1 = this; }
20
22
  anthropic;
21
23
  constructor() {
22
24
  super();
@@ -38,8 +40,8 @@ let AnthropicAiService = class AnthropicAiService extends base_ai_service_1.Base
38
40
  this._isAvailable = false;
39
41
  }
40
42
  }
41
- createAgent(configService, agentStoreService, balanceService, artifactService, agentName, agentNickname, description, promptFolder, modelName, storageDir = '') {
42
- let agent = super.createAgent(configService, agentStoreService, balanceService, artifactService, agentName, agentNickname, description, promptFolder, modelName, storageDir);
43
+ createAgent(userPaths, configService, agentStoreService, balanceService, artifactService, agentName, agentNickname, description, promptFolder, modelName, storageDir = '') {
44
+ let agent = super.createAgent(userPaths, configService, agentStoreService, balanceService, artifactService, agentName, agentNickname, description, promptFolder, modelName, storageDir);
43
45
  agent.promptCacheTTL = 5;
44
46
  return agent;
45
47
  }
@@ -211,32 +213,38 @@ let AnthropicAiService = class AnthropicAiService extends base_ai_service_1.Base
211
213
  max_tokens: maxOutputTokens,
212
214
  tools: tools.length > 0 ? tools : undefined,
213
215
  };
214
- if (agent.modelName.includes('4-7')) {
215
- createParams.temperature = 1;
216
- createParams.thinking = { type: 'adaptive' };
217
- createParams.output_config = { effort: agent.reasoningEffort };
216
+ if (this.usesEffortConfig(agent.modelName)) {
217
+ if (agent.reasoningEffort) {
218
+ createParams.thinking = { type: 'adaptive' };
219
+ createParams.output_config = { effort: agent.reasoningEffort };
220
+ }
221
+ }
222
+ else if (thinkingTokens > 0) {
223
+ createParams.thinking = { type: 'enabled', budget_tokens: thinkingTokens };
218
224
  }
219
225
  else {
220
- if (thinkingTokens > 0) {
221
- createParams.thinking = { type: 'enabled', budget_tokens: thinkingTokens };
222
- }
223
- else {
224
- createParams.thinking = { type: 'disabled' };
225
- createParams.temperature = 0;
226
- }
226
+ createParams.thinking = { type: 'disabled' };
227
+ createParams.temperature = 0;
227
228
  }
229
+ this.debugLogRequest(agent.agentName, createParams);
228
230
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
229
231
  try {
230
232
  const response = await this.anthropic.messages.create(createParams);
233
+ let thinkingText = '';
231
234
  let responseText = '';
232
235
  if (response.content && response.content.length > 0) {
233
236
  for (let i = 0; i < response.content.length; i++) {
234
237
  const content = response.content[i];
235
- if ('text' in content) {
238
+ if (content.type === 'thinking' && 'thinking' in content) {
239
+ thinkingText += content.thinking;
240
+ }
241
+ else if ('text' in content) {
236
242
  responseText += content.text;
237
243
  }
238
244
  }
239
245
  }
246
+ this.debugLogThinking(agent.agentName, thinkingText);
247
+ this.debugLogResponse(agent.agentName, responseText);
240
248
  if (jsonSchema) {
241
249
  responseText = this.trimToJson(responseText);
242
250
  responseText = this.sanitizeJsonString(responseText);
@@ -308,6 +316,10 @@ let AnthropicAiService = class AnthropicAiService extends base_ai_service_1.Base
308
316
  }
309
317
  throw new Error(`Failed to get valid response after ${maxRetries} attempts. Last error: ${lastError?.message || 'Unknown error'}`);
310
318
  }
319
+ static EFFORT_CONFIG_MODEL_VERSIONS = ['4-7', '4-8'];
320
+ usesEffortConfig(modelName) {
321
+ return AnthropicAiService_1.EFFORT_CONFIG_MODEL_VERSIONS.some((v) => modelName.includes(v));
322
+ }
311
323
  computeMessageCost(agent, usage) {
312
324
  const modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
313
325
  if (!modelInfo) {
@@ -340,7 +352,7 @@ let AnthropicAiService = class AnthropicAiService extends base_ai_service_1.Base
340
352
  }
341
353
  };
342
354
  exports.AnthropicAiService = AnthropicAiService;
343
- exports.AnthropicAiService = AnthropicAiService = __decorate([
355
+ exports.AnthropicAiService = AnthropicAiService = AnthropicAiService_1 = __decorate([
344
356
  (0, common_1.Injectable)(),
345
357
  __metadata("design:paramtypes", [])
346
358
  ], AnthropicAiService);
@@ -1,5 +1,6 @@
1
1
  import { Agent } from '../agent';
2
- import { ConfigurationService } from '../../config/configuration.service';
2
+ import { UserPathsService } from '../../config/user-paths.service';
3
+ import { IConfigService } from '../../config/env-config';
3
4
  import { ArtifactService } from '../artifact.service';
4
5
  import { AgentStoreService } from '../agent-store.service';
5
6
  import { BalanceService } from '../../app/balance.service';
@@ -20,7 +21,7 @@ export declare class BaseAiService {
20
21
  protected getPreparedSchema(agent: Agent, promptFileName: string): any;
21
22
  listModels(): Promise<ModelInfo[]>;
22
23
  protected fetchModelsFromApi(): Promise<ModelInfo[]>;
23
- createAgent(configService: ConfigurationService, agentStoreService: AgentStoreService, balanceService: BalanceService, artifactService: ArtifactService, agentName: string, agentNickname: string, description: string, promptFolder: string, modelName: string, storageDir?: string): Agent;
24
+ createAgent(userPaths: UserPathsService, configService: IConfigService, agentStoreService: AgentStoreService, balanceService: BalanceService, artifactService: ArtifactService, agentName: string, agentNickname: string, description: string, promptFolder: string, modelName: string, storageDir?: string): Agent;
24
25
  textPrompt(agent: Agent, prompt: string, _jsonSchema?: any, _saveResponse?: boolean, _promptInstructions?: string, _cacheConfig?: any, _maxOutputChars?: number): Promise<any>;
25
26
  generateImage(_agent: Agent, _prompt: string, _imageProperties?: Record<string, any>): Promise<{
26
27
  artifactId?: string;
@@ -38,6 +39,9 @@ export declare class BaseAiService {
38
39
  finishAgent(_agent: Agent): Promise<void>;
39
40
  addToConversationHistory(agent: Agent, message: string, fromSelf?: boolean, developer?: boolean): void;
40
41
  formatMessage(message: any): string;
42
+ protected debugLogRequest(agentName: string, content: unknown): void;
43
+ protected debugLogThinking(agentName: string, thinking: string): void;
44
+ protected debugLogResponse(agentName: string, response: string): void;
41
45
  trimResponseText(str: string): string;
42
46
  trimToJson(str: string): string;
43
47
  sanitizeJsonString(str: string): string;
@@ -52,6 +52,7 @@ const handlebars_1 = require("handlebars");
52
52
  const errors_1 = require("../../common/errors");
53
53
  const logger_1 = require("../../common/logger");
54
54
  const model_prices_1 = require("../model_prices");
55
+ const ai_debug_logger_1 = require("../ai-debug-logger");
55
56
  let BaseAiService = class BaseAiService {
56
57
  modelsCache = null;
57
58
  _isAvailable = true;
@@ -78,15 +79,15 @@ let BaseAiService = class BaseAiService {
78
79
  return quotedText.replace(/^['"]+|['"]+$/g, '');
79
80
  }
80
81
  findPromptFile(agent, fileName, extension) {
81
- const configService = agent.configService;
82
- if (!configService) {
83
- throw new Error('Agent configService is not set');
82
+ const userPaths = agent.userPaths;
83
+ if (!userPaths) {
84
+ throw new Error('Agent userPaths is not set');
84
85
  }
85
86
  const searchPaths = [
86
- path.join(configService.getUserPromptsDir(), agent.promptFolder, `${fileName}${extension}`),
87
- path.join(configService.getUserPromptsDir(), `${fileName}${extension}`),
88
- path.join(configService.getCommonPromptsDir(), agent.promptFolder, `${fileName}${extension}`),
89
- path.join(configService.getCommonPromptsDir(), `${fileName}${extension}`),
87
+ path.join(userPaths.getUserPromptsDir(), agent.promptFolder, `${fileName}${extension}`),
88
+ path.join(userPaths.getUserPromptsDir(), `${fileName}${extension}`),
89
+ path.join(userPaths.getCommonPromptsDir(), agent.promptFolder, `${fileName}${extension}`),
90
+ path.join(userPaths.getCommonPromptsDir(), `${fileName}${extension}`),
90
91
  ];
91
92
  for (const filePath of searchPaths) {
92
93
  if (fs.existsSync(filePath)) {
@@ -141,7 +142,7 @@ let BaseAiService = class BaseAiService {
141
142
  async fetchModelsFromApi() {
142
143
  throw new Error('fetchModelsFromApi method not implemented in base class');
143
144
  }
144
- createAgent(configService, agentStoreService, balanceService, artifactService, agentName, agentNickname, description, promptFolder, modelName, storageDir = '') {
145
+ createAgent(userPaths, configService, agentStoreService, balanceService, artifactService, agentName, agentNickname, description, promptFolder, modelName, storageDir = '') {
145
146
  if (!this.isAvailable) {
146
147
  throw new Error(`${this.getServiceName()} service is not available. Check API key configuration.`);
147
148
  }
@@ -156,7 +157,7 @@ let BaseAiService = class BaseAiService {
156
157
  throw new Error(`Model '${modelName}' is not listed in model_prices.json for ${this.getServiceName()}. ` +
157
158
  `Add a pricing entry before using this model.`);
158
159
  }
159
- const tempAgent = { promptFolder, configService };
160
+ const tempAgent = { promptFolder, userPaths };
160
161
  const systemInstructionFile = this.findPromptFile(tempAgent, 'system_instruction', '.prompt');
161
162
  let systemInstructionPrompt = null;
162
163
  if (systemInstructionFile) {
@@ -168,7 +169,7 @@ let BaseAiService = class BaseAiService {
168
169
  systemInstructionPrompt = null;
169
170
  }
170
171
  }
171
- const agent = new agent_1.Agent(null, configService, agentStoreService, balanceService, artifactService, agentName, agentNickname, description, promptFolder, systemInstructionPrompt, [], modelName, this.getServiceName(), this, null, false, 0, '', storageDir);
172
+ const agent = new agent_1.Agent(null, userPaths, configService, agentStoreService, balanceService, artifactService, agentName, agentNickname, description, promptFolder, systemInstructionPrompt, [], modelName, this.getServiceName(), this, null, false, 0, '', storageDir);
172
173
  return agent;
173
174
  }
174
175
  async textPrompt(agent, prompt, _jsonSchema = null, _saveResponse = true, _promptInstructions = null, _cacheConfig = null, _maxOutputChars = 0) {
@@ -314,6 +315,15 @@ let BaseAiService = class BaseAiService {
314
315
  }
315
316
  return JSON.stringify(message);
316
317
  }
318
+ debugLogRequest(agentName, content) {
319
+ ai_debug_logger_1.AiDebugLogger.writeRequest(agentName, content);
320
+ }
321
+ debugLogThinking(agentName, thinking) {
322
+ ai_debug_logger_1.AiDebugLogger.writeThinking(agentName, thinking);
323
+ }
324
+ debugLogResponse(agentName, response) {
325
+ ai_debug_logger_1.AiDebugLogger.writeResponse(agentName, response);
326
+ }
317
327
  trimResponseText(str) {
318
328
  if (!str) {
319
329
  return str;
@@ -187,6 +187,13 @@ let DeepSeekAiService = class DeepSeekAiService extends base_ai_service_1.BaseAi
187
187
  });
188
188
  }
189
189
  let reasoningEffort = agent.reasoningEffort ? agent.reasoningEffort : undefined;
190
+ const requestParams = {
191
+ model: agent.modelName,
192
+ messages: messages,
193
+ temperature: 0,
194
+ reasoningEffort: reasoningEffort,
195
+ };
196
+ this.debugLogRequest(agent.agentName, requestParams);
190
197
  try {
191
198
  if (jsonSchema) {
192
199
  const result = await (0, ai_1.generateObject)({
@@ -203,6 +210,7 @@ let DeepSeekAiService = class DeepSeekAiService extends base_ai_service_1.BaseAi
203
210
  if (result.usage) {
204
211
  this.computeMessageCost(agent, result.usage);
205
212
  }
213
+ this.debugLogResponse(agent.agentName, JSON.stringify(result.object, null, 2));
206
214
  if (saveResponse) {
207
215
  this.addToConversationHistory(agent, prompt, false);
208
216
  this.addToConversationHistory(agent, JSON.stringify(result.object), true);
@@ -223,6 +231,8 @@ let DeepSeekAiService = class DeepSeekAiService extends base_ai_service_1.BaseAi
223
231
  if (result.usage) {
224
232
  this.computeMessageCost(agent, result.usage);
225
233
  }
234
+ this.debugLogThinking(agent.agentName, result.reasoning ?? '');
235
+ this.debugLogResponse(agent.agentName, result.text);
226
236
  if (saveResponse) {
227
237
  this.addToConversationHistory(agent, prompt, false);
228
238
  this.addToConversationHistory(agent, result.text, true);
@@ -110,16 +110,18 @@ let GoogleAiService = class GoogleAiService extends base_ai_service_1.BaseAiServ
110
110
  generationConfig.responseSchema = schemaOnly;
111
111
  }
112
112
  const instructions = promptInstructions || agent.systemInstruction;
113
+ const requestParams = {
114
+ model: agent.modelName,
115
+ contents: messages,
116
+ config: {
117
+ systemInstruction: instructions,
118
+ ...generationConfig,
119
+ },
120
+ };
121
+ this.debugLogRequest(agent.agentName, requestParams);
113
122
  let responseText;
114
123
  try {
115
- const response = await this.googleAI.models.generateContent({
116
- model: agent.modelName,
117
- contents: messages,
118
- config: {
119
- systemInstruction: instructions,
120
- ...generationConfig,
121
- },
122
- });
124
+ const response = await this.googleAI.models.generateContent(requestParams);
123
125
  if (response.text) {
124
126
  responseText = response.text;
125
127
  }
@@ -136,6 +138,7 @@ let GoogleAiService = class GoogleAiService extends base_ai_service_1.BaseAiServ
136
138
  else {
137
139
  responseText = this.trimResponseText(responseText);
138
140
  }
141
+ this.debugLogResponse(agent.agentName, responseText);
139
142
  if (response.usageMetadata) {
140
143
  this.computeMessageCost(agent, response.usageMetadata);
141
144
  }
@@ -179,13 +179,15 @@ let GrokAiService = class GrokAiService extends base_ai_service_1.BaseAiService
179
179
  content: [{ type: 'text', text: prompt }],
180
180
  });
181
181
  }
182
+ const requestParams = {
183
+ model: agent.modelName,
184
+ messages: messages,
185
+ store: false,
186
+ };
187
+ this.debugLogRequest(agent.agentName, requestParams);
182
188
  let responseText;
183
189
  try {
184
- let response = await this.openai.chat.completions.create({
185
- model: agent.modelName,
186
- messages: messages,
187
- store: false,
188
- });
190
+ let response = await this.openai.chat.completions.create(requestParams);
189
191
  for (const choice of response.choices) {
190
192
  if (choice.message.content)
191
193
  responseText = choice.message.content;
@@ -194,6 +196,7 @@ let GrokAiService = class GrokAiService extends base_ai_service_1.BaseAiService
194
196
  responseText = this.trimToJson(responseText);
195
197
  responseText = this.sanitizeJsonString(responseText);
196
198
  }
199
+ this.debugLogResponse(agent.agentName, responseText);
197
200
  if (response.usage) {
198
201
  this.computeMessageCost(agent, response.usage);
199
202
  }