| import { |
| assertShellRunIdentifier, |
| assertShellRunPatch, |
| assertShellRunSessionId, |
| nextShellRunRecord, |
| normalizeShellRunRecord, |
| shellRunNotFoundError, |
| type ShellRunPatch, |
| type ShellRunRecord, |
| type ShellRunStore, |
| } from '@maka/core'; |
| |
| /** |
| * Task-lifetime ShellRun store for headless benchmark cells. |
| * |
| * A managed shell session only means anything while the process that owns it is |
| * alive, and a benchmark cell owns its processes for exactly one task attempt: |
| * there is no resume, no second reader, and no cross-process recovery to |
| * support. So this store keeps the records in memory instead of taking a |
| * headless storage-root lease it would never read back. |
| * |
| * The invariants are NOT re-implemented here — every rule the SQLite store |
| * enforces comes from the shared @maka/core helpers, so ShellRunProcessManager |
| * observes one state machine regardless of which store it was handed. |
| */ |
| export function createTaskShellRunStore(): ShellRunStore { |
| const runs = new Map<string, ShellRunRecord>(); |
| const key = (sessionId: string, shellRunId: string) => `${sessionId}:${shellRunId}`; |
| |
| const read = (sessionId: string, shellRunId: string): ShellRunRecord => { |
| const record = runs.get(key(sessionId, shellRunId)); |
| if (!record) throw shellRunNotFoundError(shellRunId); |
| return record; |
| }; |
| |
| return { |
| async createShellRun(record) { |
| assertShellRunSessionId(record.sessionId); |
| assertShellRunIdentifier(record.shellRunId); |
| const normalized = normalizeShellRunRecord(record, record.sessionId, record.shellRunId); |
| const recordKey = key(normalized.sessionId, normalized.shellRunId); |
| if (runs.has(recordKey)) { |
| throw new Error(`ShellRun already exists: ${normalized.shellRunId}`); |
| } |
| runs.set(recordKey, normalized); |
| return normalized; |
| }, |
| async updateShellRun(sessionId: string, shellRunId: string, patch: ShellRunPatch) { |
| assertShellRunSessionId(sessionId); |
| assertShellRunIdentifier(shellRunId); |
| assertShellRunPatch(patch); |
| const current = read(sessionId, shellRunId); |
| const next = nextShellRunRecord(current, patch); |
| if (next !== current) runs.set(key(sessionId, shellRunId), next); |
| return next; |
| }, |
| async readShellRun(sessionId: string, shellRunId: string) { |
| assertShellRunSessionId(sessionId); |
| assertShellRunIdentifier(shellRunId); |
| return read(sessionId, shellRunId); |
| }, |
| async listSessionShellRuns(sessionId: string) { |
| assertShellRunSessionId(sessionId); |
| return [...runs.values()] |
| .filter((record) => record.sessionId === sessionId) |
| .sort((left, right) => |
| left.startedAt === right.startedAt |
| ? left.shellRunId.localeCompare(right.shellRunId) |
| : left.startedAt - right.startedAt, |
| ); |
| }, |
| }; |
| } |