import type { ProcessPermissions } from '../../process/config.ts';
import { ProcessTable } from '../../process/table.ts';
import { type JsonValue } from '../../types.ts';
import type { JobStore } from './store.ts';
import { Job, type JobRunner } from './types.ts';
import { type Clock } from '../../utils/clock.ts';
import { type RecordFields } from '../../workspace/record/types.ts';
/**
 * The shell's job table: bash's per-shell job list, one list per session.
 *
 * This is job control, not a process table. A job is numbered `%N`
 * within the session that launched it, numbering restarts at 1 once that
 * session's list empties (GNU bash), and `jobs`, `wait`, `fg`, `kill` and
 * `disown` only ever see the calling session's list, exactly as one bash
 * never lists another bash's jobs. Runner PIDs are tracked separately:
 * `$!` and `jobs -l` report the managed PID, while `%N` names a
 * session-local job number. A killed job's process remains stopping until
 * the runner actually finishes, and its console ends then, or once the
 * runner had `STOP_WAIT_SECONDS` to stop and did not.
 *
 * The lists live in the workspace's job store, so every workspace on a
 * shared store sees a session's jobs with the same numbers. A job runs on
 * the table that started it, its owner; another table lists it, reads and
 * waits on its console, and kills it by ending that console, which its
 * owner watches for. `sync` brings a session's list up to date before a
 * builtin reads it. A job whose owner's lease lapsed with no ending was
 * lost with its owner and is ended as hung up, as a bash's job is when its
 * terminal goes.
 *
 * The table is still owned by the workspace rather than by a session,
 * because the workspace owns the tasks: teardown must stop every job it
 * runs in every session (`killAll`), snapshot capture reads every finished
 * one (`allJobs`), and a disowned job keeps running after its shell forgot
 * it. Those are the only cross-session views; every other method takes
 * the session whose list it reads, and the empty session id is the list a
 * caller with no session (a bare table in a test) shares.
 */
export declare class JobTable {
    readonly processes: ProcessTable;
    readonly parent: JobTable | null;
    readonly clock: Clock;
    store: JobStore;
    readonly owner: string;
    private jobs;
    private disowned;
    private lease;
    private checking;
    private stopping;
    private publishing;
    private publishTick;
    private closed;
    private reaping;
    private reaped;
    private reapingStopped;
    private swept;
    private sweepTick;
    private plane;
    private jobTables;
    /**
     * Stops the native runs a lapsed owner left, given them and the first
     * signal, and answers the ones this workspace cannot reach; the workspace
     * sets it, since it holds the runtimes that reach them.
     */
    reaper: ((runs: Record<string, JsonValue>[], signum: number) => Promise<Record<string, JsonValue>[]>) | null;
    /**
     * @param store the job plane holding each session's list and each job's
     *   console; null keeps them in memory, private to this table.
     */
    constructor(store?: JobStore | null, processes?: ProcessTable, parent?: JobTable | null, clock?: Clock);
    /**
     * A table for a child shell (`bash -c`, a script, `( )`, `$( )`): a job
     * list of its own on the same processes, kept in memory, since a child
     * shell's jobs end with it on this workspace and no other one lists
     * them. `parent` is the table a `$( )` still lists in `jobs`. It lists
     * and stops other workspaces' processes through this table, on the
     * workspace's store.
     */
    child(parent?: JobTable | null): JobTable;
    private sessionJobs;
    /**
     * Register a job in its session's list and start it.
     *
     * The job's number is taken from the store first, so two workspaces
     * starting a job on one session never share one; a process slot is
     * reserved next, and a refused one gives the number back. The runner is
     * deferred until the job and console are ready; failed setup cancels the
     * reservation without entering the supplied runner. Only fully
     * initialized jobs join the table.
     */
    submit(init: {
        command: string;
        run: JobRunner;
        abort: AbortController;
        cwd: string;
        sessionId?: string;
        parentPid?: number | null;
        limit?: number | null;
        simple?: boolean;
    }): Promise<Job>;
    /**
     * Insert a finished job restored from a snapshot into its session. Its
     * output moves onto a console in the store, so every workspace on it
     * reads the job the way it reads one that ran there. It keeps its number
     * unless another workspace's job holds it, and then takes the next free
     * one rather than hide that job.
     */
    load(job: Job): Promise<void>;
    /**
     * Bring a session's list up to date with the store.
     *
     * Adds the jobs other workspaces started, drops the ones they reaped or
     * disowned (a job this table runs keeps running, on the side list
     * `disown` uses), and settles a job whose console another workspace
     * ended, or another workspace's job whose owner's lease lapsed with no
     * ending. A job is the one listed under its number only while their
     * consoles match: once the list empties, numbering restarts and a new
     * job can take a reaped one's number.
     *
     * A console attached to a job first seen here is the job's only while
     * the list has not changed since it was read: a store drops the console
     * of a job reaped meanwhile, and attaching then would open an empty one
     * that never ends. So the list is read again after such an attach, and
     * the sync retried should it have moved. This table's own jobs are
     * matched after the last read, with no wait between, so one it started
     * meanwhile is never taken for another workspace's; a running one the
     * list no longer names stays listed, unless the list holds it as
     * disowned or another job took its number, and then it runs on unlisted,
     * as a disowned job does.
     */
    sync(sessionId?: string): Promise<void>;
    get(jobId: number, sessionId?: string): Job | null;
    listJobs(sessionId?: string): Job[];
    /** The jobs `jobs` shows: a `$( )`'s caller's, then its own. */
    listing(sessionId?: string): Job[];
    runningJobs(sessionId?: string): Job[];
    /**
     * Every session's jobs, for the workspace-wide entry points only. Snapshot
     * capture and the server summary read this; a shell builtin never
     * does, since bash lists only its own jobs.
     */
    allJobs(): Job[];
    allRunningJobs(): Job[];
    /**
     * Stop a job and record it as killed.
     *
     * The job answers as killed at once, but its console ends only once the
     * runner has stopped, so a program that handled the signal leaves its
     * own status in the ending every workspace reads. Nothing here waits for
     * the aborted runner. The signal is only observed where someone checks
     * it, which today is the executor between nodes and the commands that
     * take it, so a job sitting inside one long command would not notice
     * until it finished on its own; joining would hang the shell on exactly
     * the runaway job the caller is trying to stop. A runner that has not
     * stopped `STOP_WAIT_SECONDS` later has its console ended for it, with
     * the status the kill recorded, and one that has not begun has it ended
     * at once: no program ran that could leave its own.
     *
     * Another workspace's job is killed by ending its console; its owner
     * sees the ending and stops the runner. Should the job have ended
     * first, the ending it wrote stands and the job was not killed.
     *
     * @param signal the signal the job is stopped with; its status is 128
     *   plus it, KILL's 137 by default.
     */
    kill(jobId: number, sessionId?: string, signal?: number): Promise<boolean>;
    /**
     * Drop a job from its session's list without stopping it (`disown`):
     * the job keeps running, `jobs` no longer lists it and `wait` no
     * longer knows it. It stays on a side list so `killAll` at teardown
     * reaches its task.
     */
    disown(jobId: number, sessionId?: string): Promise<boolean>;
    /**
     * Drop a session's job list when the session closes, stopping what is
     * still running, and return what was stopped.
     *
     * What happens to a bash's jobs when that bash exits: they are hung
     * up, and a later shell that reuses the same id starts from an empty
     * list numbered from 1 rather than inheriting jobs it never launched,
     * under a profile it may not share. Session closure revokes process
     * entry points and stops disowned runners too. Closing a session closes
     * it everywhere, so the jobs other workspaces run in it are stopped too,
     * the disowned ones included: their consoles are ended, which their
     * owners watch for. The list goes only while it is the one the stops were
     * taken from: a job another workspace starts meanwhile changes it, and
     * the close stops that job too before it tries again.
     */
    closeSession(sessionId: string): Promise<Job[]>;
    /** Stop every running job this table runs in every session, returning
     * the ones that were running. Disowned jobs are stopped too: the shell
     * forgot them, the workspace did not, and a teardown that left them
     * running would leak tasks. Another workspace's jobs are its own to
     * stop. The reaping of lapsed owners' runs stops here as well, before the
     * runtimes it reaches them through close: none starts, and one under way
     * is waited for, bounded as a reap is. */
    killAll(): Promise<Job[]>;
    /**
     * Stop renewing this table's lease, at workspace teardown. Called after
     * killAll(), so every job this table ran has ended and other workspaces
     * need no lease to settle them; a killed job whose runner is still
     * stopping has its console ended now rather than at its bound. The
     * store itself belongs to the workspace's state store.
     */
    close(): Promise<void>;
    /**
     * Block until a job ends, then return it.
     *
     * Joined on the console's ending chunk, never on the status field:
     * kill() and settle() both flip the status before their final
     * appends, and every await yields a microtask, so a status-based
     * return could let the caller snapshot and reap the job before
     * `Killed` or the ending chunk is persisted. A restored job has no
     * task and its console already holds the ending chunk, so it
     * returns without waiting.
     *
     * A killed job's console ends once its runner has stopped, or
     * `STOP_WAIT_SECONDS` after the kill: what it started stops in that
     * time (a handler finishes, a sandbox is reaped) and the status it
     * leaves is the one answered, as bash's `wait` answers once the child
     * has gone. An ending another workspace wrote is taken at once, before
     * this table's watcher stops the runner. The runner is joined too, for
     * at most `STOP_WAIT_SECONDS`, so what its task holds is let go before
     * this returns.
     *
     * Another workspace's job answers the status its ending records. While
     * none is stored its owner's lease is checked every third of it, and a
     * lapsed one ends the job as hung up.
     *
     * @param signal abandons the wait, so a caller that gave up stops its
     *   reads: a promise cannot be cancelled, where Python cancels its task.
     *   The job keeps running.
     */
    wait(jobId: number, sessionId?: string, signal?: AbortSignal): Promise<Job>;
    /**
     * Join every job in a session's list, returning the ones still
     * running.
     *
     * Every job, not only the running ones: a killed job's `Killed`
     * marker can still be in flight (see wait()), and bare `wait`
     * snapshots each console right after this returns. Joining a
     * finished job costs one read.
     */
    waitAll(sessionId?: string, signal?: AbortSignal): Promise<Job[]>;
    /**
     * Remove one job from its session's list.
     *
     * What a targeted `wait`/`fg` does once the job has ended, matching
     * GNU bash, where a job waited on by id is deleted from the job list,
     * so a later `jobs` or `wait %N` no longer finds it.
     */
    reap(jobId: number, sessionId?: string): Promise<void>;
    /**
     * Return a session's completed/killed jobs and remove them from its
     * list.
     *
     * A reader holding a job's console keeps reading it: the console
     * outlives its table entry and dies with its last reader.
     */
    popCompleted(sessionId?: string): Promise<Job[]>;
    /** Another workspace's job, as its list entry describes it. */
    /**
     * Publish the processes this table's workspace runs, now that one
     * started: the publishing goes on while any runs (`publish`).
     */
    processesChanged(): void;
    /**
     * The processes other workspaces on the store run, owner to PID to
     * process, as each last published them. A lapsed owner's are left out:
     * whatever it ran was lost with it, and the native runs it left are
     * stopped meanwhile.
     */
    remoteProcesses(): Promise<Record<string, Record<string, RecordFields>>>;
    /**
     * Signal a process another workspace runs, named by a prefix of its
     * owner's id and the owner's PID: its owner stops it once it takes the
     * stop, within `PUBLISH_SECONDS`. Signal 0 only probes. A lapsed owner's
     * process is gone, and the native runs it left are stopped instead. The
     * grants are read once the store has answered, just before anything is
     * stopped, so a profile replaced meanwhile stops nothing under the old
     * one. Answers whether one live process was signalled; throws EPERM for
     * one the session sees but may not stop.
     *
     * @param owner a prefix of the owner's id, as `ps` shows it.
     * @param pid the process, by the owner's PID.
     * @param signal the signal, 0 to probe.
     * @param sessionId the calling session.
     * @param permissions whose processes it may see and stop, null once the
     *   caller's view is revoked.
     */
    signalRemote(owner: string, pid: number, signal: number, sessionId: string, permissions: () => ProcessPermissions | null): Promise<boolean>;
    private remote;
    /**
     * End a killed job's console once its runner has stopped, or
     * `STOP_WAIT_SECONDS` after the kill, whichever comes first. A runner
     * that settles ends the console itself, with the status it left; this
     * ends it with the status the kill recorded when the runner never got
     * that far (aborted before it began) or is still stopping, or at once
     * when cut (close()). Mirrors Python's `_end_killed`.
     */
    /**
     * Stop a job this table runs and record it as killed by `signal`. One
     * that has not begun has its console ended at once: no program ran that
     * could leave its own status. One that has has its console ended once its
     * runner stops, or `STOP_WAIT_SECONDS` later.
     */
    private stopRunner;
    private endKilled;
    /**
     * End a killed job's console with the status its kill recorded, unless
     * its runner already ended it.
     */
    private endStopping;
    /**
     * Keep how a job this table ran ended in its list entry, or drop its
     * disowned record, which nothing else would. The entry outlives the
     * console's output, which a shared store expires, so a workspace that
     * attaches after the output went still reads how the job ended instead
     * of taking it as hung up. A failed write is logged: the console still
     * holds the ending.
     */
    private recordEnding;
    /**
     * Settle another workspace's job from its console's ending, or end it as
     * hung up when its owner's lease lapsed with none.
     */
    private refresh;
    /**
     * Block until another workspace's job ends, or `signal` aborts. One read
     * follows the console across every lease check, so a long wait never
     * stacks readers, and an abandoned one stops reading.
     */
    private joinRemote;
    /**
     * Add a job to a session's list under the next number. Numbering
     * restarts at 1 once the list is empty, as GNU bash restarts it; without
     * that, reaping after a targeted `wait` would leave a later `wait %1`
     * pointing at nothing.
     */
    private allocate;
    /**
     * Add a job to a session's list under its own number, or under the next
     * free one should another job hold it, and return the number it took.
     */
    private insert;
    /**
     * Drop jobs from a session's list, named by their consoles, never by
     * number: another workspace may have reaped one and a new job taken its
     * number. With `giveBack` the jobs never started, so the last number
     * handed out is free again, as no job took it. With `disowned` the jobs
     * still run, disowned: their entries move to the list's disowned ones,
     * by console, where the session's close still reaches them.
     */
    private remove;
    /**
     * Write a session's list over the one read, iff still current.
     * `disowned` is the disowned jobs still running, by console; null keeps
     * the ones read. A list written where none is starts at a random
     * generation rather than at 1, so a list deleted and written again never
     * repeats the generation a reader of the old one holds: a close that read
     * the old one deletes the new one only once it has stopped its jobs.
     */
    private write;
    /**
     * Renew this table's lease while it runs jobs on a store that leases
     * owners, with the consoles of the jobs it still runs, so a quiet one
     * keeps its output while this table lives.
     */
    /** Check the lists of the jobs this table runs every
     * `LIST_CHECK_SECONDS` while it runs any. */
    private keepChecking;
    /** Stop checking once every job this table runs has ended its console. */
    private dropChecking;
    /**
     * Stop each job this table runs that its session's list dropped.
     *
     * A close, a reap and a disown of a running job each end its console or
     * keep its entry, so a list that dropped one whose console is still open
     * went with its workspace, deleted elsewhere: an ending written there
     * first would be deleted before this table's watch read it. The job is
     * stopped as a closed session's is. Each session's list is read once a
     * round, however many of its jobs run here; a round that failed for a
     * session, on its list or on a console, is tried again the next round.
     */
    private checkLists;
    /**
     * The live processes this workspace runs, as its record keeps them, each
     * with the native runs it has going.
     */
    private records;
    /**
     * Publish the processes this workspace runs every `PUBLISH_SECONDS`
     * while it runs any, and stop those other workspaces asked it to stop.
     * Once it runs none, its record goes and the publishing stops. The
     * owner's lease is renewed once ahead of the first record it vouches for,
     * then kept by the table's lease renewal (`keepLease`) while any process
     * runs, on its own timer whatever a round costs. The record is written
     * again every third of the lease, so it outlives its store's expiry and
     * comes back should a workspace have taken this one for lapsed. Each
     * record carries `seen_at`, when its processes were read, the last time
     * this workspace vouched for them and their native runs, on the host's
     * clock, which a host reap compares with `ps`. A failed
     * round is logged and tried again the next one, and a stop that fails
     * leaves the others it came with to be applied; the sleep between rounds
     * keeps no host alive on its own.
     */
    private publish;
    /**
     * Stop one of this workspace's processes another workspace asked to
     * stop: a job's runner the way `kill` stops a job, through the table
     * that holds it, a child shell's included; any other runner directly.
     */
    private stopAsked;
    /**
     * Stop what the lapsed instances on the store left running, without
     * waiting: the workspace calls it as it starts, so what a crashed instance
     * ran stops before anything lists it. Once per table, and only on a store
     * that leases owners: on any other, no owner lapses.
     */
    reapLapsed(): void;
    /**
     * Reap the lapsed owners on the store, and once more a lease later while
     * any other owner still held one: an instance that died just before this
     * one started still holds its lease until then.
     */
    private sweep;
    /** Stop the native runs a lapsed owner left, without waiting. */
    private reapLater;
    /**
     * Stop the native runs a lapsed owner left, those this workspace reaches,
     * and keep its record holding only the rest, for a workspace that reaches
     * them. The owner's lease is checked again before each run is stopped, so
     * one that renewed meanwhile keeps its runs, and its record as it is. Each
     * run goes to the reaper with its record's `seen_at`.
     */
    private reapOwner;
    private keepLease;
    /**
     * Stop renewing once every job this table runs has ended its console and
     * no process it publishes runs: a killed one still stopping keeps the
     * lease, or other workspaces would end it as hung up, and a published
     * process keeps it, or they would take its record for lapsed; the
     * publishing drops it as it stops.
     */
    private dropLease;
}
//# sourceMappingURL=table.d.ts.map