/** * The ONE roster accessor every launch path shares: the registry roster decorated with crew's * STANDING (F-2R2-009) — runtime `health`, `signed_in`, `auth`, `council_eligible` + its reason — * so `engineRosterJson` (core/engine-roster.ts) can bench a seat the daemon already knows is * unusable before the engine convenes it. (R5b: crew keeps no council bench of its own.) * * F-RECON-002 / F-RECON-003: this used to be a closure inside `registerRoutes`, so ONLY the routes * (`POST /runs`, `/testing/*`, campaigns, steering) launched with standing. The interactive seams * (draft/edit/chat/demo subscribers), the adapter's onboarding launch (`seatsForWorkflow`) and * `wicked-crew start` handed the engine `CoreAdapter.roster()` RAW — every interactive council * convened codex/pi/copilot although the roster reported them `signed_out` (9–19 councilSeatFailed, * 57–112 s of dead ballots per run), and one council ELECTED signed-out pi, which then failed its * unit and fell over to claude. The factory below is what all of those now call; `createServer` * builds exactly one over its `SeatHealthTracker` and hands the same function everywhere. * * Standing is read at CALL time (the tracker's live state, the LIVE `WICKED_WORKER_HOME`), never * captured — a seat signed in from the Agents page is eligible on the very next launch. */ import type { RosterSeat } from '../core/types.js'; import { type SeatVersionPinCache } from './diagnostics.js'; import { type SeatHealthTracker, type SeatLastAnswer } from './seat-health.js'; import { SeatProbe, type SeatProbeReading } from './seat-probe.js'; import { SeatContainerStatus, type BrokerReading } from './seat-container-login.js'; import { type SeatVersionCache } from './seat-version.js'; /** `identity` (the registry record's binary / ACP adapter / invocation) keys the memo only: the engine's predicate reads the * live registry record, so a changed binary under the same key is a different seat (codex on #1027). */ /** The addon surface the async QE batch is read from (injectable: the real napi static is not configurable). */ type QeAddon = { qeReviewersAdmitted?: (seatsJson: string) => Promise; }; /** * A container seat's sign-in reading: the credential broker's status, verified live by the seat's * own latest step on this floor. The container floor sends no live request of its own (its status * reads the broker, not the CLI), so the live check is the run path itself: a unit whose output * came back ok (a chat's answer is one) within {@link SEAT_ANSWER_WINDOW_MS} is `live`; a step that did not answer * (timed out, the CLI failed) stays `status` with `via: 'no_answer'`, which the roster warns about; * no step in the window is `status` with `via: 'broker'`, no warning: nothing was asked yet. */ export declare function containerLoginReading(broker: BrokerReading, last: SeatLastAnswer | null): SeatProbeReading; /** The accessor: the registry roster WITH crew's standing, freshly read on every call. `ready` * (crew#645), awaited by a launch before it reads the roster, waits — bounded — for every seat's * missing or stale login check, so an expired login reads `signed_out` before work is routed. */ export type RosterWithStanding = (() => RosterSeat[]) & { containerStatus?: SeatContainerStatus; ready?: () => Promise; /** crew#615: re-run one seat's credential probe now (after a sign-in / sign-out terminal). */ reprobe?: (seatKey: string) => void; /** A3b (`GET /roster?refresh=`): re-run one seat's sign-in check now and wait for it, at most * `waitMs`. `probed` — the fresh answer is in; `timed_out` — no fresh answer: still running, or * finished without being able to tell (the read serves the last answer); `not_probed` — the seat * has no sign-in check (pi, copilot, opencode). */ recheck?: (seatKey: string, waitMs: number) => Promise; }; export type RecheckOutcome = 'probed' | 'timed_out' | 'not_probed'; /** A3b: how long `GET /roster?refresh=` waits for the seat's sign-in check — the status * command (about a second) plus its one live request (a CLI start and a one-word reply). Past it * the read answers with the last reading and `timed_out`; the check keeps running. */ export declare const ROSTER_REFRESH_WAIT_MS: number; export interface RosterStandingDeps { containerStatus?: SeatContainerStatus; /** The daemon's per-seat runtime health + council evidence (councilSeatFailed, auth refusals). */ seatHealth: SeatHealthTracker; /** Seat sign-in presence probe — injectable so tests never read the developer's dotfiles. * Defaults to the file/env heuristic in seat-signin.ts. */ signedIn?: (seatKey: string, workerConfigRoot?: string) => boolean | null; /** The registry roster (default: the engine's production roster via `CoreAdapter.roster()`). * Injectable so a test decorates its own seats without the native addon. */ registry?: () => unknown[]; /** Where `WICKED_WORKER_HOME` is read from at call time (default `process.env`). */ env?: NodeJS.ProcessEnv; /** The live seat credential probe (crew#630, `seat-probe.ts`). Default: a real one when neither * `signedIn` nor `registry` is injected (the daemon), none otherwise — a test that fixes the * sign-in reading gets exactly that reading. `null` turns it off. */ probe?: SeatProbe | null; /** The seats' ACP version-pin readings (core#581, `diagnostics.ts`). Default: the daemon's * shared cache under the same rule as `probe`; `null` turns it off. */ versionPins?: SeatVersionPinCache | null; /** (A1) Every seat's `--version` reading (`seat-version.ts`). Default: the daemon's cache under * the same rule as `probe`; `null` turns it off. */ versions?: SeatVersionCache | null; /** (A5, core#920) The engine's worktree bench for a bound run: seat key → reason, or `null` when * the engine cannot answer (an addon before the predicate). Default: `CoreAdapter.worktreeBenched` * under the same rule as `probe`; `null` (or a test that injects a registry) turns it off, and * then `work_admission` and `qe_reviewer` are absent. */ worktreeBench?: ((engineSeats: readonly unknown[]) => Map | null) | null; /** (wicked-core#976) The engine's host-floor bench: seat key → reason for every seat the engine * refuses governed work on this host's floor, or `null` when the engine cannot answer. Default: * `CoreAdapter.hostFloorBenched` under the same rule as `worktreeBench`; `null` turns it off. It * is stamped as `host_floor_bench` and read by the chat and work admissions — never by * `council_eligible`, which becomes the engine's `health` and would also bench the seat from * judging and voting, calls the host floor does not refuse. */ hostFloorBench?: ((engineSeats: readonly unknown[]) => Map | null) | null; /** (A4) The engine's QE reviewer admission for one seat (`Core.qeReviewerAdmitted`), `null` when * the engine cannot answer. Default under the same rule as `worktreeBench`; without it (or with * `worktreeBench` absent) `qe_reviewer` is absent. */ qeAdmitted?: ((key: string, invocation: string | undefined) => boolean | null) | null; /** crew#1021: whole-roster admission off the daemon thread. The daemon feature-detects the * addon's async static on each read. A null entry/result uses the memoized sync fallback * (older addons block only on a miss); a batch miss serves the seat's last answer or null. */ qeAdmittedBatch?: ((seats: Array<{ key: string; invocation?: string; }>) => Promise> | null) | null; /** crew#1021: the addon the daemon default reads `Core.qeReviewersAdmitted` from at each read (default: wicked-core-ts's `Core`). */ qeAddon?: QeAddon; /** crew#1021: clock for the mutable skills fallback admission memo (30 s). */ nowMs?: () => number; /** (A6) The OS launcher the repository boundary arms with (`hostSandboxLauncher`). */ hostBoundary?: () => string | null; /** (A6) The home directory a command's display writes as `$HOME` (default `os.homedir()`). */ home?: string; } /** * Build the accessor. Each seat's registry fields ride through verbatim — the spread is * deliberately NOT a field whitelist, which is what lets the engine's `login_invocation` pass * untouched — and gains `health`, `signed_in`, and the standing readings (`auth`, `free_tier*`, * `council_eligible`, `council_ineligible_reason`, `auth_source/evidence`). */ export declare function rosterWithStandingFactory(deps: RosterStandingDeps): RosterWithStanding; export {}; //# sourceMappingURL=roster-standing.d.ts.map