/** * Acting-for — the person each agent session is working for right now * (issue #968). * * Every human message delivered to an agent records its sender as the person * that agent acts for until the next human message: a Slack message records * the sender's Slack user id (`owner` for any Slack id of the owner), a * dashboard message records the owner. A message one agent sends another * passes the sender's person on, so work delegated for Info stays Info's — and * a message an agent wrote is never recorded as a person, even when it came * through Slack under a bot's user id. Scheduled, autonomous and system turns * act for the owner. * * The record is set only by the backend from what it delivered — never from * anything an agent sends — and connector credential requests carry it to * Cloud, which decides whether that person may use the grant. * * Kept in `/acting-for.json` so a restart does not turn a * member's in-flight turn into the owner's. * * specs/per-person-access.md * * @module services/people/acting-for.service */ import { type PeopleDirectoryService, type PersonRole } from './people-directory.service.js'; /** Where an acting-for record came from. */ export type ActingForSource = 'slack' | 'dashboard' | 'agent' | 'system'; /** One session's record. */ export interface ActingForEntry { /** Person id (a member's Slack user id, or `owner`) */ personId: string; source: ActingForSource; /** ISO time it was recorded */ at: string; } /** The person a request acts for, as Cloud is told. */ export interface Actor { id: string; role: PersonRole; /** Display name, for messages */ name: string; } /** Injectable dependencies. */ export interface ActingForDeps { filePath?: string; people?: () => PeopleDirectoryService; now?: () => number; } /** The acting-for record. */ export declare class ActingForService { private readonly filePathOverride?; private readonly people; private readonly now; private cache; /** * @param deps - Store path, people directory, clock */ constructor(deps?: ActingForDeps); /** * Record that a session now acts for a person. * * @param session - Agent session name * @param personId - Slack user id, or `owner` * @param source - What delivered the message */ record(session: string, personId: string, source: ActingForSource): void; /** * Record a human message delivered to a session: a Slack sender, or the * owner when it carries no Slack sender (dashboard / terminal). * * @param session - Agent session name * @param slackUserId - Sender's Slack user id, when the message came from Slack */ recordHumanMessage(session: string, slackUserId: string | null | undefined): void; /** * A turn nobody asked for just now — a scheduled check, a scheduled * message, an autonomous run, a system event: it acts for the owner, not * for whoever spoke last. * * @param session - Agent session */ recordSystemTurn(session: string): void; /** * A message an agent wrote (in Slack, or a chat row an agent authored): * the target acts for whoever that agent acts for. When the author has no * record here (an agent on another machine) the target is left as it was. * * @param target - Receiving session * @param authorSession - The agent that wrote it */ inheritFromAgent(target: string, authorSession: string): void; /** * A message from one agent to another: the target acts for whoever the * sender acts for (the owner when the sender has no record). * * @param target - Receiving session * @param fromSession - Sending session */ inherit(target: string, fromSession: string): void; /** * A session's record. * * @param session - Agent session name * @returns Entry, or null when the session never got a human message */ get(session: string): ActingForEntry | null; /** * The person a request acts for: the owner for a request with no agent * session (the owner's dashboard), else the session's record (the owner * when it has none). * * @param session - Calling agent session, if any * @returns Actor */ actorFor(session?: string | null): Actor; /** * @param personId - Person id * @returns `owner` for the owner by any spelling, else the id */ private canonical; /** * @param id - Slack user id * @returns True for a known bot (false when the directory cannot tell) */ private isBot; /** Forget everything (tests). */ clear(): void; /** @returns Store path */ private filePath; /** @returns All records (read once, then cached) */ private load; /** * Save atomically (best effort: the in-memory record still applies). * * @param all - Records */ private save; } /** * The backend's acting-for record. * * @returns Singleton */ export declare function getActingFor(): ActingForService; /** * Replace the singleton (tests). * * @param service - Instance, or null to reset */ export declare function setActingForForTesting(service: ActingForService | null): void; /** * Before a scheduled, autonomous or system turn is delivered: the session * acts for the owner (best effort, never throws). * * @param session - Agent session the turn goes to */ export declare function noteSystemTurn(session: string): void; /** * Run work as a person: connector token requests made inside carry that * person to Cloud. * * @param actor - Person * @param fn - Work * @returns What `fn` returns */ export declare function runAsActor(actor: Actor, fn: () => T): T; /** * The person the current request acts for. * * @returns Actor, or null outside a connector request (backend-internal calls) */ export declare function currentActor(): Actor | null; /** * Headers that tell Cloud who a credential request is for. * * @param actor - Person (default: the current request's) * @returns Headers (empty outside a connector request) */ export declare function actingForHeaders(actor?: Actor | null): Record; /** * Cache-key suffix for a token fetched for the current person, so one * person's cached token is never handed to another. * * @param actor - Person (default: the current request's) * @returns `''` outside a connector request, else `\0` */ export declare function actorCacheSuffix(actor?: Actor | null): string; //# sourceMappingURL=acting-for.service.d.ts.map