@sema-agent/core
Version:
Stateless, task-oriented AI agent core
80 lines (79 loc) • 2.36 kB
JavaScript
class Seat {
sessionId;
leg;
holders = new Set();
constructor(sessionId) {
this.sessionId = sessionId;
}
get current() {
return this.leg?.current ?? "default";
}
get observed() {
return this.leg?.observed ?? "default";
}
addHolder(holder, via, signal) {
if (signal.aborted)
return;
const entry = { holder, via };
this.holders.add(entry);
signal.addEventListener("abort", () => this.holders.delete(entry), { once: true });
}
moveCeiling(affected, move) {
const before = [...this.holders].filter((e) => affected(e.via)).map((e) => [e.holder, e.holder.receipt()]);
move();
for (const [holder, receipt] of before)
holder.ceilingRebound(receipt);
}
}
class LegCeiling {
leg;
seat;
live = true;
constructor(leg, seat) {
this.leg = leg;
this.seat = seat;
}
get sessionId() {
return this.seat.sessionId;
}
get current() {
return this.live ? this.leg.current : this.seat.current;
}
get observed() {
return this.live ? this.leg.observed : (this.seat.observed ?? this.seat.current);
}
hold(holder, signal) {
this.seat.addHolder(holder, this, signal);
}
commit() {
if (this.seat.leg === this.leg)
return;
this.seat.moveCeiling((via) => !via.live, () => (this.seat.leg = this.leg));
}
end() {
if (!this.live)
return;
this.seat.moveCeiling((via) => via === this, () => (this.live = false));
}
}
export class SessionModeSeats {
seats = new Map();
reaper = new FinalizationRegistry(({ sessionId, ref }) => {
if (this.seats.get(sessionId) === ref)
this.seats.delete(sessionId);
});
seatOf(sessionId) {
const live = this.seats.get(sessionId)?.deref();
if (live !== undefined)
return live;
const seat = new Seat(sessionId);
const ref = new WeakRef(seat);
this.seats.set(sessionId, ref);
this.reaper.register(seat, { sessionId, ref });
return seat;
}
legCeiling(sessionId, leg) {
const ceiling = new LegCeiling(leg, this.seatOf(sessionId));
return { ceiling, commit: () => ceiling.commit(), end: () => ceiling.end() };
}
}