/** * daemon/pointer-controls.ts — does the session still hold the screen? * * The pointer tool gates its actuating calls behind a grant the operator makes * ("your controls"). The grant is an IDLE window: it is refreshed by every * actuating call and lapses that long after the last one. That is the right * default for a person at the machine, and the wrong one for an unattended * shift — a session that spends two hours reading code and running tests has * done nothing wrong, and finds the screen taken away when it next needs it. * * What this module exists for, though, is worse than the lapse. A session that * believed its screen time was over announced it had handed the controls back * and stopped all visual work — while the grant on disk was still valid for * another two hours. Nothing revoked anything; the belief alone cost the night. * So the manager needs to be able to answer three questions on every tick: * whether a grant exists, who holds it, and for how long — and then to say the * answer to the session in as many words, because a session that is not told * will decide for itself. * * This reads and writes the tool's own state file rather than inventing a * second record of the same fact. Two places holding "who has the screen" is * exactly the split-brain the fleet design note argues against. */ /** Where the pointer tool keeps its grant. Its format, not ours. */ export declare function controlsFile(home?: string): string; export interface Controls { /** What is written on disk. A lapsed grant still says "agent" here. */ holder: "user" | "agent"; /** When the current holder took it, ms. */ since: number; /** When an agent grant runs out, ms. Absent for the operator. */ until?: number; /** The idle window the grant was made with, minutes. */ minutes?: number; note?: string; /** No file at all — the tool has never recorded a grant on this machine. */ missing: boolean; /** An agent grant whose window has passed. The tool will refuse a click. */ lapsed: boolean; /** What the tool will actually answer right now. */ effective: "user" | "agent"; } /** * Read the grant as it stands. * * Never throws: an unreadable or malformed file means the tool will refuse a * click, and "the operator holds it" is the safe reading of that. The one thing * this must NOT do is collapse a lapsed grant into a plain operator hold — * telling those apart is the whole point, because one is ours to renew and the * other is the operator having taken the screen back. */ export declare function readControls(path?: string, now?: number): Controls; /** * Record a grant that runs until a stated moment. * * Written straight to the file rather than by typing the trigger phrase at the * session: typing only works when the session is sitting at a prompt, and the * moment a renewal matters most is the moment it is busy. The operator asked * for the grant to be held for the length of the shift they authorised, so * holding it is carrying out their instruction, not granting on their behalf. */ export declare function grantUntil(until: number, note: string, path?: string, now?: number): Controls; /** Hand the screen back. Used when a shift ends, so a grant never outlives it. */ export declare function returnToOperator(note: string, path?: string, now?: number): void; export type Verdict = /** Ours, with time on it. Nothing to do. */ { action: "hold"; why: string; } /** Ours to renew: never recorded, lapsed, or running out sooner than the shift. */ | { action: "renew"; why: string; } /** The operator took the screen back mid-shift. Their machine, their call. */ | { action: "stand-off"; why: string; }; /** * A grant with less than this left is renewed rather than watched. Comfortably * longer than a tick, so the window never closes between two looks at it. */ export declare const RENEW_FLOOR_MS: number; /** * Decide what the manager should do about the screen for a shift. * * The discriminator between "lapsed" and "the operator took it back" is which * holder is written on disk: a lapse leaves an agent grant with a past expiry * (nothing rewrites it), while the operator taking it back writes an operator * hold with a fresh timestamp. So an operator hold that started AFTER the shift * did is a deliberate act and is left alone; anything older is the state a * shift is expected to overwrite. */ export declare function verdict(c: Controls, shiftStartedAt: number, shiftUntil: number, now?: number, floorMs?: number): Verdict; /** One line for the status report: who has the screen, and for how much longer. */ export declare function describeControls(c: Controls, now?: number): string; /** * What to tell the session, in the goal, when it holds the screen. * * Positive and with a time on it, because the failure this answers was a * session inventing an expiry and standing down two hours early. Silence in the * goal about the screen reads, to a session that has been careful about the * operator's machine all night, as permission having quietly ended. * * Two parts, and only one of them is load-bearing here. THE EXPIRY is state the * session cannot obtain any other way — the grant lives on disk, the session * never sees it, so a goal that omits the time invites the invented one. THE * GUIDANCE around it is ordinary instruction about how to behave, and an * operator whose objective already carries that — a goal template, a rules file * — is having it said twice, in the one place where every wasted word is * retyped at every arming. * * So `terse` drops the guidance and keeps the fact. It is never the other way * round: there is no configuration in which the manager grants the screen and * declines to say until when. */ export declare function screenGrantedClause(until: number, terse?: boolean): string; //# sourceMappingURL=pointer-controls.d.ts.map