/**
 * A background agent's CURRENT state — the one reading every read face renders.
 *
 * A parent model answers "how far along is it?" from what its read faces show. Three faces read a
 * background agent: AgentTranscript (its recent steps), ListAgents (the roster) and TaskOutput (the poll).
 * Each one projects {@link readAgentState}'s result and renders it with {@link agentStateSummary} /
 * {@link agentActivityClause} / {@link agentApprovalClause}; none derives status, activity time or approval
 * facts on its own, so the three cannot disagree about one agent at one instant.
 *
 * Sources:
 *  - the registry row — the live handle this engine process runs ({@link agentStateSourceOfHandle}), or the
 *    durable record when no live handle answers ({@link agentStateSourceOfRecord});
 *  - for a row whose status is `parked` only, the approval checkpoint the row names, read through the run's
 *    checkpoint seat (the same seat the Agent tool parks children into). The checkpoint token is the resume
 *    capability and never leaves this module: a face renders the checkpoint's non-secret `checkpointId`.
 *
 * Absence is stated, never left silent: a missing activity time renders as "last activity unknown (…why)",
 * and a parked row whose card cannot be read carries `approval.state` `missing` / `unreadable`. A face that
 * goes quiet on a fact invites the reader to fill the gap with a guess.
 */
import type { SemaTaskStatus } from "./task-registry-shared.js";
import { type CheckpointSeat } from "./checkpoint-seat.js";
/** The approval card's state: the checkpoint row's own `status`, plus `missing` (the row names no card, or
 *  the store holds none under its token) and `unreadable` (no checkpoint seat on this face, or the store
 *  threw). The same five answers the parked-row reconciliation sweep gives the same question. */
export type AgentApprovalState = "pending" | "resolved" | "expired" | "missing" | "unreadable";
/** The approval checkpoint a parked agent waits on. */
export interface AgentApproval {
    state: AgentApprovalState;
    /** The checkpoint's non-secret identity (the card id a person sees in the approval inbox). Present when
     *  the card was read and carries one. */
    checkpointId?: string;
    /** The card's SLA / abandonment deadline, epoch ms. Present when the card was read and carries one. */
    deadline?: number;
}
/** Where the row was read from: the live handle this engine process runs, or the durable record (the
 *  process that wrote it may be another one, or gone). */
export type AgentStateRow = "live_row" | "durable_row";
/** One agent's state at {@link readAt}. Times are epoch ms; relative ages appear only in rendered text. */
export interface AgentState {
    /** The registry row's own status word. A word outside {@link SemaTaskStatus} reads `unknown`. */
    status: SemaTaskStatus | "unknown";
    /** The instant this state was read (epoch ms) — the base every rendered age is computed from. */
    readAt: number;
    row: AgentStateRow;
    /** The row's last recorded change (epoch ms). Live row: every state transition and every tool start /
     *  end of the direct child moves it. Durable row: when it parked (`parked`) or settled (terminal).
     *  ABSENT when the source holds no such time — a durable row still marked running (activity in another
     *  process is not observable here), or a row that never recorded one. */
    lastActivityAt?: number;
    /** Present exactly when {@link status} is `parked`. */
    approval?: AgentApproval;
}
/** The row facts {@link readAgentState} reads. Built by the two projections below — never by a face. */
export interface AgentStateSource {
    status: string;
    row: AgentStateRow;
    lastActivityAt?: number;
    parkedCheckpointToken?: string;
}
/** The live handle's facts: `updatedAt` is moved by every state transition and by each direct-child tool
 *  lifecycle beat, so it is the row's last activity. */
export declare function agentStateSourceOfHandle(h: {
    status: string;
    updatedAt: number;
    parkedCheckpointToken?: string;
}): AgentStateSource;
/** The durable record's facts. Its `updatedAt` is the writer's liveness lease renewal, not activity, so it
 *  is never read as such: a parked record answers with `parkedAt`, a terminal one with `settledAt`, and a
 *  record still marked running answers nothing. */
export declare function agentStateSourceOfRecord(r: {
    status: string;
    parkedAt?: number;
    settledAt?: number;
    parkedCheckpointToken?: string;
}): AgentStateSource;
/**
 * THE state of one background agent at `now`. Synchronous unless the row is parked on a card the seat can
 * read — only that row takes an async frame (the store read); every other row is answered in place.
 * The caller must already have passed the row through its face's access check: this reads the card the
 * row names and authorizes nothing.
 */
export declare function readAgentState(src: AgentStateSource, seat: CheckpointSeat | undefined, now: number): AgentState | Promise<AgentState>;
/** `Ns` / `Nm` / `Nh` / `Nd` — a bounded age word; a negative span reads as `0s`. */
export declare function formatAgo(ms: number): string;
/** `last activity <age> ago`, or the reason the time is unknown. */
export declare function agentActivityClause(s: AgentState): string;
/** `approval <card id> <state>[ (deadline in <age> | deadline passed <age> ago)]` — facts only. */
export declare function agentApprovalClause(a: AgentApproval, readAt: number): string;
/** The one-line state sentence: `<status> · <activity clause>[ · <approval clause>]`. */
export declare function agentStateSummary(s: AgentState): string;
