import api from './api'; // These types intentionally mirror the durable session API. Tracks are not // embedded in a plan revision: the client resolves ids through the normal // library endpoint so a deleted/hidden track can never become playable merely // because an older plan mentioned it. export interface VibePlanItem { plan_version_id: string; ordinal: number; track_id: string; slot_role: string | null; candidate_source: string; score: number; score_breakdown: Record; explanation: unknown; committed: boolean; } export interface DurableVibeSessionResponse { sessionId: string; planVersion: number | null; now: VibePlanItem | null; preview: VibePlanItem[]; state: Record; replanned: boolean; replanReason: string | null; } export type VibeEventType = | 'playback_started' | 'progress' | 'completed' | 'skipped' | 'disliked' | 'kept'; export interface VibeEventInput { eventId: string; type: VibeEventType; trackId?: string; occurredAt: string; positionMs?: number; durationMs?: number; payload?: Record; } export interface VibeEventResponse extends DurableVibeSessionResponse { event: { id: string; client_event_id: string | null; type: string }; idempotent: boolean; } /** A durable, idempotent advancement past a plan item the player cannot load. */ export interface VibeUnplayableItemInput { eventId: string; planVersionId: string; ordinal: number; trackId: string; } export const vibeService = { async start(seedTrackId?: string): Promise { const res = await api.post('/v2/vibe/sessions', { seedTrackId }); return res.data; }, async getPlan(sessionId: string, version?: number): Promise { const res = await api.get(`/v2/vibe/sessions/${sessionId}/plans`, { params: version === undefined ? undefined : { version }, }); return res.data; }, async next(sessionId: string, expectedPlanVersion: number): Promise { const res = await api.post(`/v2/vibe/sessions/${sessionId}/next`, { expectedPlanVersion, }); return res.data; }, async advancePastUnplayable( sessionId: string, expectedPlanVersion: number, unplayable: VibeUnplayableItemInput, ): Promise { const res = await api.post(`/v2/vibe/sessions/${sessionId}/next`, { expectedPlanVersion, unplayable, }); return res.data; }, async event(sessionId: string, event: VibeEventInput): Promise { const res = await api.post(`/v2/vibe/sessions/${sessionId}/events`, event); return res.data; }, async end(sessionId: string): Promise { const res = await api.post(`/v2/vibe/sessions/${sessionId}/end`); return res.data; }, };