102 lines
3.0 KiB
TypeScript
102 lines
3.0 KiB
TypeScript
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<string, unknown>;
|
|
explanation: unknown;
|
|
committed: boolean;
|
|
}
|
|
|
|
export interface DurableVibeSessionResponse {
|
|
sessionId: string;
|
|
planVersion: number | null;
|
|
now: VibePlanItem | null;
|
|
preview: VibePlanItem[];
|
|
state: Record<string, unknown>;
|
|
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<string, unknown>;
|
|
}
|
|
|
|
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<DurableVibeSessionResponse> {
|
|
const res = await api.post<DurableVibeSessionResponse>('/v2/vibe/sessions', { seedTrackId });
|
|
return res.data;
|
|
},
|
|
|
|
async getPlan(sessionId: string, version?: number): Promise<DurableVibeSessionResponse> {
|
|
const res = await api.get<DurableVibeSessionResponse>(`/v2/vibe/sessions/${sessionId}/plans`, {
|
|
params: version === undefined ? undefined : { version },
|
|
});
|
|
return res.data;
|
|
},
|
|
|
|
async next(sessionId: string, expectedPlanVersion: number): Promise<DurableVibeSessionResponse> {
|
|
const res = await api.post<DurableVibeSessionResponse>(`/v2/vibe/sessions/${sessionId}/next`, {
|
|
expectedPlanVersion,
|
|
});
|
|
return res.data;
|
|
},
|
|
|
|
async advancePastUnplayable(
|
|
sessionId: string,
|
|
expectedPlanVersion: number,
|
|
unplayable: VibeUnplayableItemInput,
|
|
): Promise<DurableVibeSessionResponse> {
|
|
const res = await api.post<DurableVibeSessionResponse>(`/v2/vibe/sessions/${sessionId}/next`, {
|
|
expectedPlanVersion,
|
|
unplayable,
|
|
});
|
|
return res.data;
|
|
},
|
|
|
|
async event(sessionId: string, event: VibeEventInput): Promise<VibeEventResponse> {
|
|
const res = await api.post<VibeEventResponse>(`/v2/vibe/sessions/${sessionId}/events`, event);
|
|
return res.data;
|
|
},
|
|
|
|
async end(sessionId: string): Promise<DurableVibeSessionResponse> {
|
|
const res = await api.post<DurableVibeSessionResponse>(`/v2/vibe/sessions/${sessionId}/end`);
|
|
return res.data;
|
|
},
|
|
};
|