blob: 3e00d65b7de20fe0ee258d62fb468f4bbda9a714 [file]
/**
* What a `sessions:create` request resolves to.
*
* #1433: these fields used to be derived in two places. `sessions:create`
* took them from the renderer, and a second IPC (`quickChat:start`, built for
* the first-run Quick Chat panel) derived them from a product `mode`. The
* panel is gone, and what remained of the second IPC was a duplicate of the
* first — same readiness gate, same connection resolution, same
* `emitSessionsChanged('created')` — so only the derivation survived.
*
* It lives here as a pure function rather than inside the handler because the
* handler is an `ipcMain.handle` closure no test can call. Every invariant
* below — the mode's boundary outranking both the renderer's request and the
* configured default, the refusal of a directly-requested `explore`, the
* settings-backed fallback that must never reject — would otherwise only be
* assertable by regex over the handler's source.
*/
import type {
AppSettings,
CollaborationMode,
OrchestrationMode,
PermissionMode,
SessionStartMode,
} from '@maka/core';
import {
DEFAULT_SESSION_NAME,
isChatDefaultPermissionMode,
isCollaborationMode,
isOrchestrationMode,
isSessionStartMode,
sessionStartModeSpec,
} from '@maka/core';
import { resolveDefaultPermissionMode } from './permission-mode-default.js';
/**
* `unknown`, because this is an IPC boundary and the renderer's type is a
* promise, not a guarantee. An unrecognized value confers nothing — it is not
* a mode — and the caller falls through to an ordinary session, which is the
* same session it would have got by not naming one.
*/
export interface CreateSessionRequest {
mode?: SessionStartMode;
permissionMode?: PermissionMode;
collaborationMode?: CollaborationMode;
orchestrationMode?: OrchestrationMode;
name?: string;
labels?: string[];
}
export interface ResolvedCreateSessionInput {
permissionMode: PermissionMode;
collaborationMode: CollaborationMode;
orchestrationMode: OrchestrationMode;
name: string;
labels: string[] | undefined;
}
export interface ResolvedCreateSessionRequest
extends Omit<ResolvedCreateSessionInput, 'permissionMode'> {
mode?: SessionStartMode;
permissionMode?: PermissionMode;
}
export function resolveCreateSessionRequest(
input: CreateSessionRequest | undefined,
): ResolvedCreateSessionRequest {
const collaborationMode = input?.collaborationMode ?? 'agent';
if (!isCollaborationMode(collaborationMode)) {
throw new TypeError('Invalid collaboration mode.');
}
const orchestrationMode = input?.orchestrationMode ?? 'default';
if (!isOrchestrationMode(orchestrationMode)) {
throw new TypeError('Invalid orchestration mode.');
}
// `explore` is a boundary a product mode confers, not one a caller may
// request directly. Existing Sessions use a separate deliberate mutation.
if (input?.permissionMode !== undefined && !isChatDefaultPermissionMode(input.permissionMode)) {
throw new TypeError('Invalid permission mode.');
}
return {
...(isSessionStartMode(input?.mode) ? { mode: input.mode } : {}),
...(input?.permissionMode === undefined ? {} : { permissionMode: input.permissionMode }),
collaborationMode,
orchestrationMode,
name: input?.name ?? DEFAULT_SESSION_NAME,
labels: input?.labels,
};
}
export async function resolveCreateSessionInput(
input: CreateSessionRequest | undefined,
deps: { readSettings: () => Promise<AppSettings> },
): Promise<ResolvedCreateSessionInput> {
const request = resolveCreateSessionRequest(input);
const mode = request.mode === undefined ? undefined : sessionStartModeSpec(request.mode);
return {
collaborationMode: request.collaborationMode,
orchestrationMode: request.orchestrationMode,
name: mode?.name ?? request.name,
labels:
mode === undefined
? request.labels
: [...new Set([...(request.labels ?? []), ...mode.labels])],
permissionMode:
mode?.permissionMode ??
request.permissionMode ??
(await resolveDefaultPermissionMode(deps.readSettings)),
};
}