blob: 7fccbb280151331fb50010b741a2c286a0d05ef9 [file]
import type { ThinkingLevel } from '@maka/core';
import type { PermissionMode } from '@maka/core';
import type { SessionSummary } from '@maka/core';
export interface MakaRunSessionSelectionInput {
sessions: readonly SessionSummary[];
resumeId?: string;
continueLatest: boolean;
explicitCwd?: string;
processCwd: string;
explicitConnection?: string;
explicitModel?: string;
thinkingSpecified: boolean;
explicitThinking?: ThinkingLevel;
explicitPermissionMode?: Exclude<PermissionMode, 'ask'>;
}
export type MakaRunSessionSelection =
| { kind: 'new'; cwd: string }
| { kind: 'existing'; cwd: string; session: SessionSummary };
export interface MakaRunSessionSelectionDeps {
canonicalizeDirectory(path: string): Promise<string>;
}
export async function selectMakaRunSession(
input: MakaRunSessionSelectionInput,
deps: MakaRunSessionSelectionDeps,
): Promise<MakaRunSessionSelection> {
if (input.resumeId !== undefined && input.continueLatest) {
throw new Error('--resume and --continue cannot be used together');
}
if (input.resumeId !== undefined) return selectExplicitSession(input, deps);
if (input.continueLatest) return selectLatestSession(input, deps);
return {
kind: 'new',
cwd: await deps.canonicalizeDirectory(input.explicitCwd ?? input.processCwd),
};
}
async function selectExplicitSession(
input: MakaRunSessionSelectionInput & { resumeId?: string },
deps: MakaRunSessionSelectionDeps,
): Promise<Extract<MakaRunSessionSelection, { kind: 'existing' }>> {
const session = input.sessions.find((candidate) => candidate.id === input.resumeId);
if (!session) throw new Error(`session not found: ${input.resumeId}`);
assertSupportedSession(session);
const cwd = await canonicalSessionCwd(session, deps);
if (input.explicitCwd !== undefined) {
const explicitCwd = await deps.canonicalizeDirectory(input.explicitCwd);
if (explicitCwd !== cwd) {
throw new Error(`--cwd conflicts with resumed session ${session.id}`);
}
}
assertExplicitConfigurationCompatible(session, input);
return { kind: 'existing', cwd, session };
}
async function selectLatestSession(
input: MakaRunSessionSelectionInput,
deps: MakaRunSessionSelectionDeps,
): Promise<Extract<MakaRunSessionSelection, { kind: 'existing' }>> {
const cwd = await deps.canonicalizeDirectory(input.explicitCwd ?? input.processCwd);
const candidates = input.sessions.filter(isContinueCandidate).sort((left, right) => {
const timeDelta = right.lastMessageAt! - left.lastMessageAt!;
return timeDelta !== 0 ? timeDelta : left.id.localeCompare(right.id);
});
for (const session of candidates) {
const sessionCwd = await tryCanonicalSessionCwd(session, deps);
if (sessionCwd !== cwd) continue;
assertExplicitConfigurationCompatible(session, input);
return { kind: 'existing', cwd: sessionCwd, session };
}
throw new Error(`no compatible session found for cwd: ${cwd}`);
}
function isContinueCandidate(session: SessionSummary): boolean {
return (
session.subagentParent === undefined &&
!session.isArchived &&
(session.status === 'active' || session.status === 'aborted') &&
typeof session.lastMessageAt === 'number' &&
Number.isFinite(session.lastMessageAt) &&
session.backend === 'ai-sdk'
);
}
function assertSupportedSession(session: SessionSummary): void {
if (session.backend !== 'ai-sdk') {
throw new Error(`session ${session.id} uses unsupported backend: ${session.backend}`);
}
}
function assertExplicitConfigurationCompatible(
session: SessionSummary,
input: Pick<
MakaRunSessionSelectionInput,
| 'explicitConnection'
| 'explicitModel'
| 'thinkingSpecified'
| 'explicitThinking'
| 'explicitPermissionMode'
>,
): void {
if (
input.explicitConnection !== undefined &&
input.explicitConnection !== session.llmConnectionSlug
) {
throw new Error(`--connection conflicts with resumed session ${session.id}`);
}
if (input.explicitModel !== undefined && input.explicitModel !== session.model) {
throw new Error(`--model conflicts with resumed session ${session.id}`);
}
if (input.thinkingSpecified && input.explicitThinking !== session.thinkingLevel) {
throw new Error(`--thinking conflicts with resumed session ${session.id}`);
}
if (
input.explicitPermissionMode !== undefined &&
input.explicitPermissionMode !== session.permissionMode
) {
throw new Error(`--permission-mode conflicts with resumed session ${session.id}`);
}
}
async function canonicalSessionCwd(
session: SessionSummary,
deps: MakaRunSessionSelectionDeps,
): Promise<string> {
if (!session.cwd) throw new Error(`session ${session.id} has no stored cwd`);
try {
return await deps.canonicalizeDirectory(session.cwd);
} catch {
throw new Error(`session ${session.id} cwd is missing or inaccessible: ${session.cwd}`);
}
}
async function tryCanonicalSessionCwd(
session: SessionSummary,
deps: MakaRunSessionSelectionDeps,
): Promise<string | undefined> {
try {
return await canonicalSessionCwd(session, deps);
} catch {
return undefined;
}
}