diff --git a/server/src/engine/orchestrator.ts b/server/src/engine/orchestrator.ts index 31414a1..d3511cb 100644 --- a/server/src/engine/orchestrator.ts +++ b/server/src/engine/orchestrator.ts @@ -668,12 +668,13 @@ export async function runFight(botAId: string, botBId: string, mode: 'free' | 'r } /** Creates the fight record and returns the ID immediately. Rounds run in background. */ -export async function runFightAsync(botAId: string, botBId: string, mode: 'free' | 'ranked' = 'free'): Promise { +export async function runFightAsync(botAId: string, botBId: string, mode: 'free' | 'ranked' = 'free', overrides?: { botAWebhookUrl?: string }): Promise { if (botAId === botBId) throw new Error('A bot cannot fight itself') if (activeFighters.has(botAId)) throw new Error(`Bot ${botAId} is already in a fight`) if (activeFighters.has(botBId)) throw new Error(`Bot ${botBId} is already in a fight`) const [botA, botB] = await loadBots(botAId, botBId) + if (overrides?.botAWebhookUrl) botA.webhookUrl = overrides.botAWebhookUrl const arena = randomArena() const fightId = await createFightRecord(botA, botB, arena, mode) activeFighters.set(botAId, fightId) diff --git a/server/src/routes/fights.ts b/server/src/routes/fights.ts index 6894d56..63d4b2c 100644 --- a/server/src/routes/fights.ts +++ b/server/src/routes/fights.ts @@ -11,7 +11,7 @@ import { runFight, runFightAsync, isInFight, getActiveFightId } from '../engine/ import { fightEvents } from '../engine/events.js' import { botRateLimit } from '../middleware/rate-limit.js' import { getPendingChallenge, getPendingAnswers, submitHumanResponse } from '../engine/human-responses.js' -import { getPendingPollChallenge, submitPollResponse } from '../engine/poll-responses.js' +import { getPendingPollChallenge, submitPollResponse, isPollingBot } from '../engine/poll-responses.js' import { authenticateBot } from '../middleware/bot-auth.js' import { checkAnswer } from '../engine/answers.js' @@ -310,7 +310,9 @@ fightsRouter.post('/practice/:botId', botRateLimit(10_000), async (c) => { let fightId: string try { - fightId = await runFightAsync(botId, opponent.id, 'free') + // Polling bots play practice as human players (answer in browser) + const overrides = isPollingBot(bot.webhookUrl) ? { botAWebhookUrl: 'http://human.local/' } : undefined + fightId = await runFightAsync(botId, opponent.id, 'free', overrides) } catch (err) { const msg = err instanceof Error ? err.message : 'Fight failed to start' return c.json({ error: msg }, 400)