batched round: queue actions/diplomacy, jump forward commits one turn (v0.2.0)
CI / build + unit + e2e (pull_request) Successful in 1m51s

This commit is contained in:
2026-10-11 14:36:35 +00:00
parent 3297658c81
commit 9bce14254d
16 changed files with 563 additions and 246 deletions
+20 -4
View File
@@ -2,7 +2,14 @@
// proxied by Vite in dev and served by Express in prod), so no origin is ever
// hardcoded — this works unchanged behind the Traefik proxy.
import type { GameState, Preset, PresetMeta } from '@shared/types';
import type { AdvisorResponse, CreateGameResponse, GameSummary, TurnResponse } from '@shared/protocol';
import type {
AddActionResponse,
AddDiplomacyResponse,
AdvisorResponse,
CreateGameResponse,
GameSummary,
TurnResponse,
} from '@shared/protocol';
async function req<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(path, {
@@ -28,11 +35,20 @@ export const api = {
// Every saved game as a lightweight summary, newest-updated first (for the
// home screen's "Saved games" section / resume affordances).
listGames: () => req<{ games: GameSummary[] }>('/api/games'),
turn: (id: string, action?: string, chat?: { to: string; message: string }) =>
req<TurnResponse>(`/api/games/${id}/turn`, {
// Queue a free-text player action onto the pending round. No LLM call — the
// round is only committed by `jump`. Returns the updated game (with pending).
addAction: (id: string, text: string) =>
req<AddActionResponse>(`/api/games/${id}/action`, {
method: 'POST',
body: JSON.stringify({ action, chat }),
body: JSON.stringify({ text }),
}),
// Queue a diplomatic message to a target nation onto the pending round.
addDiplomacy: (id: string, to: string, message: string) =>
req<AddDiplomacyResponse>(`/api/games/${id}/diplomacy`, {
method: 'POST',
body: JSON.stringify({ to, message }),
}),
// Commit the whole pending round (or an empty one) in a single LLM turn.
jump: (id: string) =>
req<TurnResponse>(`/api/games/${id}/jump`, {
method: 'POST',