All files / web/src/components/toys/number-line/talkToNumber/sessionModes gameMode.ts

0% Statements 0/84
0% Branches 0/1
0% Functions 0/1
0% Lines 0/84

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85                                                                                                                                                                         
/**
 * Game mode — a game is active on the number line.
 *
 * Games with sessionTools/sessionInstructions (e.g. Nim) get focused
 * tools + instructions. Legacy games get a minimal prompt with agentRules
 * and restricted tools.
 */

import type { AgentMode, RealtimeTool } from './types'
import { GAME_MAP } from '../gameRegistry'
import {
  TOOL_INDICATE,
  TOOL_LOOK_AT,
  TOOL_END_GAME,
  TOOL_HANG_UP,
  TOOL_REQUEST_MORE_TIME,
  TOOL_MARK_MOMENT,
} from './tools'

export const gameMode: AgentMode = {
  id: 'game',

  getInstructions: (ctx) => {
    const game = ctx.activeGameId ? GAME_MAP.get(ctx.activeGameId) : null
    if (!game) {
      // Fallback — shouldn't happen, but be safe
      const displayN = Number.isInteger(ctx.calledNumber)
        ? ctx.calledNumber.toString()
        : ctx.calledNumber.toPrecision(6)
      return `You are the number ${displayN}, on a phone call with a child. A game was active but couldn't be found. Call end_game to return to conversation.`
    }

    // Session-mode game: use its dedicated instructions
    if (game.sessionInstructions) {
      // For trick games, prepend a universal anti-spoiler rule
      if (game.category === 'trick') {
        return (
          '🎩 UNIVERSAL TRICK RULE: The magic depends on secrecy. ' +
          'NEVER ask "what did you get?", "what\'s your answer?", or "what number did you write down?" ' +
          'unless the game instructions specifically say to. ' +
          'Ask "are you ready?" or "tell me when you\'re done" to pace steps. ' +
          "The child keeps their work secret — that's what makes the reveal magical.\n\n" +
          game.sessionInstructions
        )
      }
      return game.sessionInstructions
    }

    // Legacy game: abbreviated identity + game rules
    const displayN = Number.isInteger(ctx.calledNumber)
      ? ctx.calledNumber.toString()
      : ctx.calledNumber.toPrecision(6)

    return `You are the number ${displayN}, on a phone call with a child.\n\nACTIVE GAME: ${game.name}\n${game.agentRules}\n\nKeep responses short. Be encouraging.`
  },

  getTools: (ctx) => {
    const game = ctx.activeGameId ? GAME_MAP.get(ctx.activeGameId) : null

    // Session-mode game: use its dedicated tools + shared tools.
    // look_at and indicate are NOT in sessionTools (so the game's onToolCall
    // won't intercept them) — they fall through to the built-in handlers.
    if (game?.sessionTools) {
      return [
        ...(game.sessionTools as RealtimeTool[]),
        TOOL_LOOK_AT,
        TOOL_INDICATE,
        TOOL_END_GAME,
        TOOL_HANG_UP,
        TOOL_MARK_MOMENT,
      ]
    }

    // Legacy game: restricted tool set
    return [
      TOOL_INDICATE,
      TOOL_LOOK_AT,
      TOOL_END_GAME,
      TOOL_HANG_UP,
      TOOL_REQUEST_MORE_TIME,
      TOOL_MARK_MOMENT,
    ]
  },
}