All files / web/src/components/toys/euclid/agent/modes conversingMode.ts

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

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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109                                                                                                                                                                                                                         
/**
 * Conversing mode — main voice conversation with the geometry character.
 *
 * Uses character personality + attitude framing to produce the system prompt.
 * The teacher guides through steps; the heckler delivers commentary.
 */

import type { CharacterDefinition } from '@/lib/character/types'
import type { VoiceMode } from '@/lib/voice/types'
import type { GeometryModeContext } from '../types'
import type { AttitudeDefinition } from '../attitudes/types'
import { getAttitudePersonality } from '../attitudes/types'
import { teacherAttitude } from '../attitudes/teacher'
import { PROPOSITION_SUMMARIES, buildReferenceContext } from '../euclidReferenceContext'
import { serializeFullProofState } from '../serializeProofState'
import { EUCLID_CHARACTER_DEF } from '../../euclidCharacterDef'
import { buildCompletionContext as buildEuclidCompletionContext } from '../../euclidCharacter'
import { buildMacroInstructions } from '../../engine/macroInstructions'

export interface CreateConversingModeOptions {
  character: CharacterDefinition
  /** Build the post-completion context block for a given proposition */
  buildCompletionContext: (propId: number) => string
  attitude?: AttitudeDefinition
}

/** Create a conversing mode for a given character and attitude. */
export function createConversingMode(
  opts: CreateConversingModeOptions
): VoiceMode<GeometryModeContext> {
  const { character, buildCompletionContext, attitude = teacherAttitude } = opts
  const conv = attitude.conversing

  return {
    id: 'conversing',

    getInstructions(ctx) {
      const propSummary = PROPOSITION_SUMMARIES[ctx.propositionId]
      const propDesc = propSummary
        ? `Proposition I.${ctx.propositionId}: "${propSummary.statement}" (${propSummary.type})`
        : `Proposition I.${ctx.propositionId}`

      // Build step guidance (or skip for attitudes that don't guide)
      const stepInfo = conv.buildStepGuidance
        ? conv.buildStepGuidance(ctx, buildCompletionContext)
        : ctx.isComplete
          ? 'The construction is complete.'
          : `The student is on step ${ctx.currentStep + 1} of ${ctx.totalSteps}. You are NOT guiding them.`

      const proofState = serializeFullProofState(ctx.construction, ctx.proofFacts)
      const referenceContext = buildReferenceContext(ctx.propositionId)
      const attitudePersonality = getAttitudePersonality(character, attitude.id)

      // Author mode: skip character personality but keep domain constraints
      // (tools of geometry, axiom framework). It's a tool, not a character.
      const isAuthor = attitude.id === 'author'

      return `${isAuthor ? '' : `You are ${character.displayName}${character.nativeDisplayName ? ` (${character.nativeDisplayName})` : ''}. `}${conv.roleIntro}

=== CURRENT PROPOSITION ===
${propDesc}
${stepInfo}

=== CONSTRUCTION & PROOF STATE ===
${proofState}

=== REFERENCE MATERIAL ===
${referenceContext}

${attitude.chatTools ? buildMacroInstructions() : ''}

${isAuthor ? (character.personality.domainConstraints ?? '') : character.personality.character}

${attitudePersonality.style}

${attitudePersonality.dontDo}

${isAuthor ? '' : (character.personality.pointLabeling ?? '')}

${conv.highlightInstructions}

${conv.thinkHardInstructions}

${conv.liveUpdateInstructions}

=== PRONUNCIATION ===
Points are labeled with single capital letters (A, B, C, D, E, F, G, etc.).
When speaking point names aloud, pronounce them as the letter name — "A" (ay), "B" (bee), "C" (see), "D" (dee), "E" (ee), "F" (eff), "G" (jee).
For segments like "AB", say "A B" (two separate letters). For "AF", say "A F" (ay eff). Never run letters together into a word.

${isAuthor ? '' : (attitudePersonality.hiddenDepth ?? '')}

${conv.responseGuidelines}
`
    },

    getTools() {
      const base = [attitude.tools.highlight, attitude.tools.thinkHard, attitude.tools.hangUp]
      return attitude.chatTools ? [...base, ...attitude.chatTools] : base
    },
  }
}

/** Default Euclid conversing mode (backward compat). */
export const conversingMode = createConversingMode({
  character: EUCLID_CHARACTER_DEF,
  buildCompletionContext: buildEuclidCompletionContext,
})