Makelore 2.0 initial clean snapshot
This commit is contained in:
215
src/stores/opencode-session-run-machine.ts
Normal file
215
src/stores/opencode-session-run-machine.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
export type SessionRunQueuedPrompt = {
|
||||
id: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export type SessionRunPhase = 'idle' | 'posting' | 'running' | 'aborting';
|
||||
|
||||
export type SessionRunTerminalReason = 'completed' | 'aborted' | 'failed';
|
||||
|
||||
export type SessionRunState<TPrompt extends SessionRunQueuedPrompt = SessionRunQueuedPrompt> = {
|
||||
phase: SessionRunPhase;
|
||||
runId: number | null;
|
||||
promptId: string | null;
|
||||
queue: TPrompt[];
|
||||
terminalReason: SessionRunTerminalReason | null;
|
||||
error: string | null;
|
||||
suppressNextAbortError: boolean;
|
||||
};
|
||||
|
||||
export type SessionRunEvent<TPrompt extends SessionRunQueuedPrompt = SessionRunQueuedPrompt> =
|
||||
| { type: 'send_started'; runId: number; promptId: string }
|
||||
| { type: 'post_accepted'; runId: number }
|
||||
| { type: 'remote_busy'; runId: number }
|
||||
| { type: 'remote_idle'; runId: number }
|
||||
| { type: 'remote_aborted'; runId: number }
|
||||
| { type: 'remote_failed'; runId: number; error: string }
|
||||
| { type: 'abort_requested'; runId: number }
|
||||
| { type: 'abort_confirmed'; runId: number }
|
||||
| { type: 'abort_failed'; runId: number; error: string }
|
||||
| { type: 'queue_prompt'; prompt: TPrompt }
|
||||
| { type: 'cancel_queued_prompt'; promptId: string };
|
||||
|
||||
export function createIdleSessionRunState<TPrompt extends SessionRunQueuedPrompt>(
|
||||
queue: TPrompt[] = [],
|
||||
): SessionRunState<TPrompt> {
|
||||
return {
|
||||
phase: 'idle',
|
||||
runId: null,
|
||||
promptId: null,
|
||||
queue,
|
||||
terminalReason: null,
|
||||
error: null,
|
||||
suppressNextAbortError: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function isCurrentRunEvent<TPrompt extends SessionRunQueuedPrompt>(
|
||||
state: SessionRunState<TPrompt>,
|
||||
runId: number,
|
||||
): boolean {
|
||||
return state.runId === runId;
|
||||
}
|
||||
|
||||
export function isSessionRunActive<TPrompt extends SessionRunQueuedPrompt>(
|
||||
state: SessionRunState<TPrompt>,
|
||||
): boolean {
|
||||
return state.phase === 'posting' || state.phase === 'running';
|
||||
}
|
||||
|
||||
export function canStartQueuedPrompt<TPrompt extends SessionRunQueuedPrompt>(
|
||||
state: SessionRunState<TPrompt>,
|
||||
): boolean {
|
||||
return state.phase === 'idle'
|
||||
&& state.terminalReason !== 'failed'
|
||||
&& state.queue.length > 0;
|
||||
}
|
||||
|
||||
export function takeNextQueuedPrompt<TPrompt extends SessionRunQueuedPrompt>(
|
||||
state: SessionRunState<TPrompt>,
|
||||
): {
|
||||
prompt: TPrompt | null;
|
||||
state: SessionRunState<TPrompt>;
|
||||
} {
|
||||
const [prompt, ...queue] = state.queue;
|
||||
return {
|
||||
prompt: prompt ?? null,
|
||||
state: {
|
||||
...state,
|
||||
queue,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function didSuppressAbortError<TPrompt extends SessionRunQueuedPrompt>(
|
||||
previousState: SessionRunState<TPrompt>,
|
||||
nextState: SessionRunState<TPrompt>,
|
||||
): boolean {
|
||||
return previousState.suppressNextAbortError
|
||||
&& !nextState.suppressNextAbortError
|
||||
&& previousState.phase === nextState.phase
|
||||
&& previousState.runId === nextState.runId
|
||||
&& previousState.promptId === nextState.promptId
|
||||
&& nextState.terminalReason === previousState.terminalReason
|
||||
&& nextState.error === null;
|
||||
}
|
||||
|
||||
export function transitionSessionRunState<TPrompt extends SessionRunQueuedPrompt>(
|
||||
state: SessionRunState<TPrompt>,
|
||||
event: SessionRunEvent<TPrompt>,
|
||||
): SessionRunState<TPrompt> {
|
||||
if (event.type === 'send_started') {
|
||||
return {
|
||||
phase: 'posting',
|
||||
runId: event.runId,
|
||||
promptId: event.promptId,
|
||||
queue: state.queue,
|
||||
terminalReason: null,
|
||||
error: null,
|
||||
suppressNextAbortError: state.suppressNextAbortError,
|
||||
};
|
||||
}
|
||||
|
||||
if (event.type === 'queue_prompt') {
|
||||
return {
|
||||
...state,
|
||||
queue: [...state.queue, event.prompt],
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (event.type === 'cancel_queued_prompt') {
|
||||
return {
|
||||
...state,
|
||||
queue: state.queue.filter((prompt) => prompt.id !== event.promptId),
|
||||
};
|
||||
}
|
||||
|
||||
if (!isCurrentRunEvent(state, event.runId)) {
|
||||
return state;
|
||||
}
|
||||
|
||||
if (
|
||||
state.phase === 'idle'
|
||||
&& state.terminalReason !== null
|
||||
&& (
|
||||
event.type === 'post_accepted'
|
||||
|| event.type === 'remote_busy'
|
||||
|| event.type === 'remote_idle'
|
||||
)
|
||||
) {
|
||||
return state;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case 'post_accepted':
|
||||
case 'remote_busy':
|
||||
return {
|
||||
...state,
|
||||
phase: 'running',
|
||||
terminalReason: null,
|
||||
error: null,
|
||||
};
|
||||
case 'remote_idle':
|
||||
return {
|
||||
...state,
|
||||
phase: 'idle',
|
||||
terminalReason: 'completed',
|
||||
error: null,
|
||||
suppressNextAbortError: false,
|
||||
};
|
||||
case 'remote_aborted':
|
||||
if (state.suppressNextAbortError) {
|
||||
return {
|
||||
...state,
|
||||
error: null,
|
||||
suppressNextAbortError: false,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
phase: 'idle',
|
||||
queue: [],
|
||||
terminalReason: 'aborted',
|
||||
error: null,
|
||||
suppressNextAbortError: false,
|
||||
};
|
||||
case 'remote_failed':
|
||||
return {
|
||||
...state,
|
||||
phase: 'idle',
|
||||
terminalReason: 'failed',
|
||||
error: event.error,
|
||||
suppressNextAbortError: false,
|
||||
};
|
||||
case 'abort_requested':
|
||||
return {
|
||||
...state,
|
||||
phase: 'aborting',
|
||||
queue: [],
|
||||
terminalReason: 'aborted',
|
||||
error: null,
|
||||
suppressNextAbortError: true,
|
||||
};
|
||||
case 'abort_confirmed':
|
||||
return {
|
||||
...state,
|
||||
phase: 'idle',
|
||||
queue: [],
|
||||
terminalReason: 'aborted',
|
||||
error: null,
|
||||
suppressNextAbortError: false,
|
||||
};
|
||||
case 'abort_failed':
|
||||
return {
|
||||
...state,
|
||||
phase: 'idle',
|
||||
queue: [],
|
||||
terminalReason: 'failed',
|
||||
error: event.error,
|
||||
suppressNextAbortError: false,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user