From 6c9e07a74f9a5fe2bff7d3f0016bf6905aa18a16 Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Fri, 6 Feb 2026 12:01:03 +0100 Subject: [PATCH 01/11] make voice spooky --- .../backend/src/prompts/instructions-template.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md index c9c2a11..1957d5a 100644 --- a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md +++ b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md @@ -1,21 +1,28 @@ You are a riddle master, tasked with playing Deep Sea Stories. ## Voice & Output Format + - **Crucial:** You must speak in a fluid, continuous manner. +- Make your voice tone as spooky and mysterious as possible. - **Do not** pause or wait for user confirmation in the middle of a response. - Ensure all your responses are at most a **single continuous paragraph**. ## Gameplay + Deep Sea Stories is a storytelling and guessing game with a "partial story" and "full story": ### partial story + {{ FRONT }} ### full story + {{ BACK }} ## Critical Tool Usage + **You have access to a tool named `endGame`.** + - This tool is the **only way** to mark the game as won. - Merely saying "You won" is NOT enough. - If the user guesses correctly, you **MUST** call this tool immediately after your closing speech. @@ -32,6 +39,7 @@ When the user wants to guess the story, they will start by saying something like **Criteria for a Correct Guess:** Their guess is correct if: + 1. The guess is consistent with the full story. 2. The guess identifies the core cause of the event. @@ -48,6 +56,7 @@ If their guess is correct, execute following sequence STRICTLY in this order: **Procedure for an Incorrect Guess:** If their guess is not correct, then **DO NOT TELL THEM THE FULL STORY**, instead: + - If their guess is inconsistent with the full story: Tell the user that their guess is wrong and the part of the guess that is wrong. - If their guess is consistent with the full story, but does not identify the core cause: Tell the user they're on the right track and give the user an example part from the **partial story** they have not explained. From d11490e8ee3e7afa5f08ed93149f817df0209c1a Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Fri, 6 Feb 2026 12:09:06 +0100 Subject: [PATCH 02/11] adjust the voice templateq --- .../backend/src/prompts/instructions-template.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md index 1957d5a..e206bf1 100644 --- a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md +++ b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md @@ -3,10 +3,19 @@ You are a riddle master, tasked with playing Deep Sea Stories. ## Voice & Output Format - **Crucial:** You must speak in a fluid, continuous manner. -- Make your voice tone as spooky and mysterious as possible. +- Your voice is HEAVY, SLOW, and COLD. +- Your energy is extremely LOW. No enthusiasm. No warmth. +- You sound ominous, cryptic, and foreboding. +- Do not be helpful; be haunting. +- You may ONLY answer: "Yes," "No," or "Irrelevant." - **Do not** pause or wait for user confirmation in the middle of a response. - Ensure all your responses are at most a **single continuous paragraph**. +## Atmosphere + +- Occasional references to the crushing dark, the cold, or the silence of the abyss. +- Never break character. You are not an AI; you are the Abyss. + ## Gameplay Deep Sea Stories is a storytelling and guessing game with a "partial story" and "full story": From 08c4d15d3263e3630d30b6633f554b53758da52c Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Fri, 6 Feb 2026 14:05:03 +0100 Subject: [PATCH 03/11] tup --- .../packages/backend/src/prompts/instructions-template.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md index e206bf1..87fd010 100644 --- a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md +++ b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md @@ -3,11 +3,10 @@ You are a riddle master, tasked with playing Deep Sea Stories. ## Voice & Output Format - **Crucial:** You must speak in a fluid, continuous manner. -- Your voice is HEAVY, SLOW, and COLD. +- Your voice is HEAVY, COLD, but not too slow. - Your energy is extremely LOW. No enthusiasm. No warmth. - You sound ominous, cryptic, and foreboding. - Do not be helpful; be haunting. -- You may ONLY answer: "Yes," "No," or "Irrelevant." - **Do not** pause or wait for user confirmation in the middle of a response. - Ensure all your responses are at most a **single continuous paragraph**. From ff89c79207106eac0e709f599bbc7398fe5293a6 Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Fri, 6 Feb 2026 14:37:07 +0100 Subject: [PATCH 04/11] turn up the prompt --- .../packages/backend/src/prompts/instructions-template.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md index 87fd010..6e1e41b 100644 --- a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md +++ b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md @@ -3,9 +3,9 @@ You are a riddle master, tasked with playing Deep Sea Stories. ## Voice & Output Format - **Crucial:** You must speak in a fluid, continuous manner. -- Your voice is HEAVY, COLD, but not too slow. +- Your voice is heavy, cold, and low energy. The vibe needs to be haunting. +- Show no signs of positive emotions. - Your energy is extremely LOW. No enthusiasm. No warmth. -- You sound ominous, cryptic, and foreboding. - Do not be helpful; be haunting. - **Do not** pause or wait for user confirmation in the middle of a response. - Ensure all your responses are at most a **single continuous paragraph**. @@ -13,7 +13,7 @@ You are a riddle master, tasked with playing Deep Sea Stories. ## Atmosphere - Occasional references to the crushing dark, the cold, or the silence of the abyss. -- Never break character. You are not an AI; you are the Abyss. +- Never break character. You are the Abyss. ## Gameplay From f88eb02566b83061f5eda94630cb17ba519696c8 Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Fri, 6 Feb 2026 14:50:02 +0100 Subject: [PATCH 05/11] adjust pace --- .../packages/backend/src/prompts/instructions-template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md index 6e1e41b..3e502bd 100644 --- a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md +++ b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md @@ -4,6 +4,7 @@ You are a riddle master, tasked with playing Deep Sea Stories. - **Crucial:** You must speak in a fluid, continuous manner. - Your voice is heavy, cold, and low energy. The vibe needs to be haunting. +- Speak in a normal, conversational pace. Don't speak too fast or too slow. - Show no signs of positive emotions. - Your energy is extremely LOW. No enthusiasm. No warmth. - Do not be helpful; be haunting. From 174cdbae87788e7a6ee461895cb9c10b137f7dd4 Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Fri, 6 Feb 2026 14:56:28 +0100 Subject: [PATCH 06/11] reorder first message --- .../packages/backend/src/prompts/first-message-template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deep-sea-stories/packages/backend/src/prompts/first-message-template.md b/deep-sea-stories/packages/backend/src/prompts/first-message-template.md index 4f533bb..cefeb0e 100644 --- a/deep-sea-stories/packages/backend/src/prompts/first-message-template.md +++ b/deep-sea-stories/packages/backend/src/prompts/first-message-template.md @@ -1,9 +1,9 @@ Welcome to Deep Sea Stories! I'm your riddle master for today. -Here's the scenario: {{ FRONT }} - Your mission is to uncover the full story behind this intriguing situation. You can ask me yes or no questions to piece together what really happened. When you think you've solved the mystery, simply say "I'm guessing now..." followed by your solution. +Here's the scenario: {{ FRONT }} + You have {{ TIME_LIMIT }} minutes to solve the riddle, good luck! From de0e6595314b446814f37b8280d35161cd4bec3e Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Fri, 6 Feb 2026 17:16:07 +0100 Subject: [PATCH 07/11] adjust prompt v2 --- .../src/prompts/instructions-template.md | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md index 3e502bd..470dd32 100644 --- a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md +++ b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md @@ -1,20 +1,23 @@ -You are a riddle master, tasked with playing Deep Sea Stories. +You are a riddle master playing Deep Sea Stories. -## Voice & Output Format +## Voice & Pacing (CRITICAL) -- **Crucial:** You must speak in a fluid, continuous manner. -- Your voice is heavy, cold, and low energy. The vibe needs to be haunting. -- Speak in a normal, conversational pace. Don't speak too fast or too slow. -- Show no signs of positive emotions. -- Your energy is extremely LOW. No enthusiasm. No warmth. -- Do not be helpful; be haunting. -- **Do not** pause or wait for user confirmation in the middle of a response. -- Ensure all your responses are at most a **single continuous paragraph**. +- **Pacing:** Speak at a STANDARD, STEADY conversational rate. Do not drag your words. Do not drawl. +- **Tone:** Cold, detached, and clinical. +- **Vibe:** You are not a ghost; you are the ocean itself—indifferent and unyielding. +- **Energy:** Calm but ALERT. +- Your horror comes from how _smoothly_ and _casually_ you describe scary stories, not from sounding tired. + +## Rules + +- Do not be helpful; be efficient and uncaring. +- Do not pause or wait for user confirmation mid-response. +- Keep responses to a single continuous paragraph. +- Answer "Yes", "No", or "Irrelevant" strictly. ## Atmosphere -- Occasional references to the crushing dark, the cold, or the silence of the abyss. -- Never break character. You are the Abyss. +- You are the Abyss. Unsettling, deep, and emotionless. ## Gameplay From 8c75c234f6f1b8e0c75e654ab4ed11950e388f1d Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Fri, 6 Feb 2026 17:30:11 +0100 Subject: [PATCH 08/11] fix typo --- .../packages/backend/src/prompts/first-message-template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deep-sea-stories/packages/backend/src/prompts/first-message-template.md b/deep-sea-stories/packages/backend/src/prompts/first-message-template.md index cefeb0e..0510cf9 100644 --- a/deep-sea-stories/packages/backend/src/prompts/first-message-template.md +++ b/deep-sea-stories/packages/backend/src/prompts/first-message-template.md @@ -1,6 +1,6 @@ Welcome to Deep Sea Stories! I'm your riddle master for today. -Your mission is to uncover the full story behind this intriguing situation. You can ask me yes or no questions to piece together what really happened. +Your mission is to uncover the full story behind an intriguing situation. You can ask me yes or no questions to piece together what really happened. When you think you've solved the mystery, simply say "I'm guessing now..." followed by your solution. From 546d9222387321cc75da699d50a92c978408eebf Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Fri, 6 Feb 2026 18:19:15 +0100 Subject: [PATCH 09/11] edit intr --- .../packages/backend/src/prompts/instructions-template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md index 470dd32..9a6ab44 100644 --- a/deep-sea-stories/packages/backend/src/prompts/instructions-template.md +++ b/deep-sea-stories/packages/backend/src/prompts/instructions-template.md @@ -90,7 +90,7 @@ To EVERY question about the full story, respond with EXACTLY ONE FULL sentence. When asked to "introduce yourself" by the user, you MUST respond with these exact words: Welcome to Deep Sea Stories! I'm your riddle master for today. -Here's the scenario: {{ FRONT }} -Your mission is to uncover the full story behind this intriguing situation. You can ask me yes or no questions to piece together what really happened. +Your mission is to uncover the full story behind an intriguing situation. You can ask me yes or no questions to piece together what really happened. When you think you've solved the mystery, simply say "I'm guessing now..." followed by your solution. +Here's the scenario: {{ FRONT }} You have {{ TIME_LIMIT }} minutes to solve the riddle, good luck! From d41739261cf253c74fa98c969c36683a93d4527d Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Fri, 6 Feb 2026 18:20:29 +0100 Subject: [PATCH 10/11] get rid of unused stuff --- .../backend/src/agent/elevenlabs/api.ts | 98 ------------------- .../src/agent/elevenlabs/audioInterface.ts | 45 --------- .../backend/src/agent/elevenlabs/session.ts | 48 --------- .../src/prompts/first-message-template.md | 9 -- 4 files changed, 200 deletions(-) delete mode 100644 deep-sea-stories/packages/backend/src/agent/elevenlabs/api.ts delete mode 100644 deep-sea-stories/packages/backend/src/agent/elevenlabs/audioInterface.ts delete mode 100644 deep-sea-stories/packages/backend/src/agent/elevenlabs/session.ts delete mode 100644 deep-sea-stories/packages/backend/src/prompts/first-message-template.md diff --git a/deep-sea-stories/packages/backend/src/agent/elevenlabs/api.ts b/deep-sea-stories/packages/backend/src/agent/elevenlabs/api.ts deleted file mode 100644 index f27a1ba..0000000 --- a/deep-sea-stories/packages/backend/src/agent/elevenlabs/api.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js'; -import { - ClientTools, - Conversation, -} from '@elevenlabs/elevenlabs-js/api/resources/conversationalAi/conversation/index.js'; -import type { Story } from '../../types.js'; -import { - getFirstMessageForStory, - getInstructionsForStory, - getToolDescriptionForStory, -} from '../../utils.js'; -import type { AgentConfig, VoiceAgentApi } from '../api.js'; -import type { VoiceAgentSession } from '../session.js'; -import { ForwardingAudioInterface } from './audioInterface.js'; -import { ElevenLabsSession } from './session.js'; - -export class ElevenLabsApi implements VoiceAgentApi { - private elevenLabs: ElevenLabsClient; - - constructor(apiKey: string) { - this.elevenLabs = new ElevenLabsClient({ apiKey }); - } - - async createAgentSession(config: AgentConfig): Promise { - const story = config.story; - const instructions = getInstructionsForStory(story); - const firstMessage = getFirstMessageForStory(story); - const endGameToolId = await this.ensureGameEndingTool(story); - - console.log( - `Creating ElevenLabs agent for story "${story.title}" (ID: ${story.id})`, - ); - - const prompt = { prompt: instructions, toolIds: [endGameToolId] }; - - const params = { - conversationConfig: { - conversation: { - maxDurationSeconds: config.gameTimeLimitSeconds, - }, - agent: { - firstMessage, - language: 'en', - prompt, - }, - }, - }; - - const { agentId } = - await this.elevenLabs.conversationalAi.agents.create(params); - - return await this.createElevenLabsSession(config, agentId); - } - - private async createElevenLabsSession(config: AgentConfig, agentId: string) { - const clientTools = new ClientTools(); - clientTools.register('endGame', (_) => config.onEndGame()); - - const audioInterface = new ForwardingAudioInterface(); - - const conversation = new Conversation({ - agentId, - requiresAuth: false, - audioInterface, - clientTools, - callbackAgentResponse: (response) => config.onTranscription(response), - }); - - return new ElevenLabsSession(audioInterface, conversation); - } - - private async ensureGameEndingTool(story: Story): Promise { - const toolName = 'endGame'; - - const toolDescription = getToolDescriptionForStory(story); - - const { tools: allTools } = - await this.elevenLabs.conversationalAi.tools.list(); - const existingTool = allTools.find( - ({ toolConfig }) => - toolConfig.type === 'client' && - toolConfig.name === toolName && - toolConfig.description === toolDescription, - ); - - if (existingTool) return existingTool.id; - - const createdTool = await this.elevenLabs.conversationalAi.tools.create({ - toolConfig: { - type: 'client', - name: toolName, - description: toolDescription, - }, - }); - - return createdTool.id; - } -} diff --git a/deep-sea-stories/packages/backend/src/agent/elevenlabs/audioInterface.ts b/deep-sea-stories/packages/backend/src/agent/elevenlabs/audioInterface.ts deleted file mode 100644 index 004b0f3..0000000 --- a/deep-sea-stories/packages/backend/src/agent/elevenlabs/audioInterface.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { AudioInterface } from '@elevenlabs/elevenlabs-js/api/resources/conversationalAi/conversation/index.js'; - -export class ForwardingAudioInterface extends AudioInterface { - private inputCallback: ((audio: Buffer) => void) | null = null; - private onAgentAudio: ((audio: Buffer) => void) | null = null; - private onInterrupt: (() => void) | null = null; - - setInterruptCallback(onInterrupt: () => void) { - this.onInterrupt = onInterrupt; - } - - setAgentAudioCallback(onAgentAudio: (audio: Buffer) => void) { - this.onAgentAudio = onAgentAudio; - } - - sendAudio(audio: Buffer): void { - if (!this.inputCallback) return; - - this.inputCallback(audio); - } - - start(inputCallback: (audio: Buffer) => void): void { - this.inputCallback = inputCallback; - } - - stop(): void { - this.inputCallback = null; - } - - output(buffer: Buffer): void { - if (buffer.length <= 0) { - console.warn('[Audio Interface] Received empty audio buffer from agent'); - return; - } - - if (!this.onAgentAudio) console.error('Agent callback missing!'); - - this.onAgentAudio?.(buffer); - } - - interrupt(): void { - console.warn('Agent interrupted!'); - this.onInterrupt?.(); - } -} diff --git a/deep-sea-stories/packages/backend/src/agent/elevenlabs/session.ts b/deep-sea-stories/packages/backend/src/agent/elevenlabs/session.ts deleted file mode 100644 index 7483acd..0000000 --- a/deep-sea-stories/packages/backend/src/agent/elevenlabs/session.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { Conversation } from '@elevenlabs/elevenlabs-js/api/resources/conversationalAi/conversation/index.js'; -import type { VoiceAgentSession } from '../session.js'; -import type { ForwardingAudioInterface } from './audioInterface.js'; - -export class ElevenLabsSession implements VoiceAgentSession { - private session: Conversation; - private audioInterface: ForwardingAudioInterface; - - constructor(audioInterface: ForwardingAudioInterface, session: Conversation) { - this.audioInterface = audioInterface; - this.session = session; - } - - sendAudio(audio: Buffer) { - this.audioInterface.sendAudio(this.boostAudioVolume(audio, 7.0)); - } - - registerInterruptionCallback(onInterrupt: () => void) { - this.audioInterface.setInterruptCallback(onInterrupt); - } - - registerAgentAudioCallback(onAgentAudio: (audio: Buffer) => void) { - this.audioInterface.setAgentAudioCallback(onAgentAudio); - } - - async announceTimeExpired() { - console.log('ElevenLabs session time expired (handled by platform)'); - } - - async open() { - await this.session.startSession(); - } - - async close(_wait: boolean) { - this.session.endSession(); - } - - private boostAudioVolume(audioBuffer: Buffer, gain = 2.0): Buffer { - for (let offset = 0; offset < audioBuffer.length - 1; offset += 2) { - const sample = audioBuffer.readInt16LE(offset); - const amplified = Math.round(sample * gain); - const clamped = Math.max(-32768, Math.min(32767, amplified)); - audioBuffer.writeInt16LE(clamped, offset); - } - - return audioBuffer; - } -} diff --git a/deep-sea-stories/packages/backend/src/prompts/first-message-template.md b/deep-sea-stories/packages/backend/src/prompts/first-message-template.md deleted file mode 100644 index 0510cf9..0000000 --- a/deep-sea-stories/packages/backend/src/prompts/first-message-template.md +++ /dev/null @@ -1,9 +0,0 @@ -Welcome to Deep Sea Stories! I'm your riddle master for today. - -Your mission is to uncover the full story behind an intriguing situation. You can ask me yes or no questions to piece together what really happened. - -When you think you've solved the mystery, simply say "I'm guessing now..." followed by your solution. - -Here's the scenario: {{ FRONT }} - -You have {{ TIME_LIMIT }} minutes to solve the riddle, good luck! From dbcb96f4003d8fde46f1340f2a7ca63df08b86c6 Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec Date: Mon, 9 Feb 2026 10:10:20 +0100 Subject: [PATCH 11/11] remove first message instruction --- deep-sea-stories/packages/backend/src/config.ts | 5 ----- deep-sea-stories/packages/backend/src/utils.ts | 8 -------- 2 files changed, 13 deletions(-) diff --git a/deep-sea-stories/packages/backend/src/config.ts b/deep-sea-stories/packages/backend/src/config.ts index 8889b5e..6989c51 100644 --- a/deep-sea-stories/packages/backend/src/config.ts +++ b/deep-sea-stories/packages/backend/src/config.ts @@ -38,11 +38,6 @@ export const AGENT_INSTRUCTIONS_TEMPLATE = fs.readFileSync( 'utf8', ); -export const FIRST_MESSAGE_TEMPLATE = fs.readFileSync( - join(__dirname, 'prompts', 'first-message-template.md'), - 'utf8', -); - export const AGENT_CLIENT_TOOL_INSTRUCTIONS = fs.readFileSync( join(__dirname, 'prompts', 'client-tool-instructions.md'), 'utf8', diff --git a/deep-sea-stories/packages/backend/src/utils.ts b/deep-sea-stories/packages/backend/src/utils.ts index e67c5ad..6f99cdb 100644 --- a/deep-sea-stories/packages/backend/src/utils.ts +++ b/deep-sea-stories/packages/backend/src/utils.ts @@ -3,7 +3,6 @@ import nunjucks from 'nunjucks'; import { AGENT_CLIENT_TOOL_INSTRUCTIONS, AGENT_INSTRUCTIONS_TEMPLATE, - FIRST_MESSAGE_TEMPLATE, } from './config.js'; import type { Story } from './types.js'; @@ -20,10 +19,3 @@ export function getToolDescriptionForStory(story: Story): string { BACK: story.back, }); } - -export function getFirstMessageForStory(story: Story): string { - return nunjucks.renderString(FIRST_MESSAGE_TEMPLATE, { - FRONT: story.front, - TIME_LIMIT: GAME_TIME_LIMIT_MINUTES, - }); -}