Repository navigation
Expand file tree
/
Copy pathsession-store.ts
More file actions
49 lines (43 loc) · 1.78 KB
/
Copy pathsession-store.ts
File metadata and controls
49 lines (43 loc) · 1.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import { canTransition, type SessionStatus, type StartOptions } from "@codehands/contracts";
// Only what the runner needs to stop, resume, diff, approve or reject.
// The daemon owns the real database and the upload.
export interface SessionRecord {
options: StartOptions;
status: SessionStatus;
worktreePath?: string;
repoKey?: string; // set by start(). Where this repo's memory lives
branch?: string;
baseCommit?: string;
pid?: number;
stopRequested?: boolean; // set by stop(), so the agent's exit reads as task.stopped, not task.failed
lastSeq: number;
diff?: { patch: string; stat: string };
}
export class SessionStore {
private sessions = new Map<string, SessionRecord>();
create(options: StartOptions): SessionRecord {
if (this.sessions.has(options.sessionId)) throw new Error(`session ${options.sessionId} already exists`);
const record: SessionRecord = { options, status: "queued", lastSeq: 0 };
this.sessions.set(options.sessionId, record);
return record;
}
get(sessionId: string): SessionRecord {
const record = this.sessions.get(sessionId);
if (!record) throw new Error(`unknown session ${sessionId}`);
return record;
}
// Refuses any move not in SESSION_TRANSITIONS, e.g. approving a running session.
transition(sessionId: string, to: SessionStatus): SessionRecord {
const record = this.check(sessionId, to);
record.status = to;
return record;
}
// The same check without moving, for work that must happen before the move (commit, then approved).
check(sessionId: string, to: SessionStatus): SessionRecord {
const record = this.get(sessionId);
if (!canTransition(record.status, to)) {
throw new Error(`illegal move for ${sessionId}: ${record.status} -> ${to}`);
}
return record;
}
}