import { spawnSync } from "node:child_process"; import type { H2ALaunchContext } from "../../../session.js"; import type { H2ADriveRequest, H2ADriver } from "../../drive/index.js"; import { type H2ADrumbeatEntry } from "../../drumbeat/registry.js"; import { type RelauncherRuntime } from "../../drumbeat/relaunchers.js"; import { type H2ALoopLaunchSpec, type H2AObjectiveLoop } from "../index.js"; import { type AgentsSnapshot, type InboxSnapshot, type PendingDecision, type PresenceSnapshot, type RefsRollup, type RefStatus } from "./decision.js"; import type { ActionSink } from "./execute.js"; export declare function readAgents(): Promise; /** * Resolve the self-declared work status the pure R3 idle gate consumes. The * presence FILE never carries `workStatus`: the host stop hook records it in the * durable drumbeat REGISTRY (`h2a drumbeat record --status …` → `recordStop`), * NOT in presence (see drumbeat/registry.ts). Without folding the registry back * in, the R3 gate's `paused`/`out-of-tokens` fast-path is permanently dead and a * just-stopped agent is never relanced until 15 min of MCP silence — the bug * behind "the objective-loop does not relance like /loop". * * A durable stop entry is honored ONLY when it is not terminal (an * escalated/rerouted stop is resolved) AND has NOT been superseded by fresher MCP * activity: if the agent has made a tool call since it declared the stop * (`lastMcpActivityAt > stoppedAt`) it has resumed, so the sticky `paused` entry * must NOT re-wake it — that is exactly the over-wake the R3 gate guards against. * A live presence `workStatus` (should the presence layer ever set one) always * wins over the registry. PURE: timestamps in, status out. */ export declare function resolveDeclaredWorkStatus(entry: H2ADrumbeatEntry | undefined, lastActivityAtMs: number | undefined): string | undefined; export declare function readPresenceSnapshot(root: string, now: number): PresenceSnapshot; /** Shape of a `track query --format json` row we consume. */ interface TrackQueryRow { readonly id?: string; readonly bucket?: string; readonly realization?: string; readonly acceptance?: string; } /** * PURE mapping: a track item's (bucket, realization, acceptance) → loop RefStatus. * Buckets: AWAITED | DROPPED | DONE | TO-DO. Conservative + unit-tested so the * semantics stay explicit and easy to tune. Unknown/missing → "unknown" (the * core treats that as NOT satisfied → never auto-closes on it). */ export declare function mapTrackAggregateToRefStatus(item: TrackQueryRow | undefined): RefStatus; export declare function readRefsRollup(loop: H2AObjectiveLoop): RefsRollup; /** PURE: escalations in an inbox → pending decisions for `agentId`. */ export declare function pendingDecisionsFromInbox(envelopes: readonly { readonly id: string; readonly type: string; }[], agentId: string): PendingDecision[]; export declare function readInbox(loop: H2AObjectiveLoop, root: string): InboxSnapshot; export type WakePlan = { readonly kind: "wake"; readonly instance: string; readonly host?: string; readonly launchContext: H2ALaunchContext; readonly instructionLine: string; } | { readonly kind: "skip"; readonly reason: string; }; /** * PURE: given the loop, a target agent id, the FRESH sessions (already * expiry-filtered by listPresence) and prior wake timestamps, decide whether to * wake (and with what launchContext/line) or skip (and why). No IO. Freshness + * cooldown are the safety gates BEFORE the driver's own human-typing guard. */ export declare function planWakeTarget(input: { readonly loop: H2AObjectiveLoop; readonly agentId: string; readonly freshSessions: readonly { readonly instance: string; readonly launchContext?: H2ALaunchContext; }[]; readonly priorWakeAtByAgent: ReadonlyMap; readonly priorWakeCountByAgent?: ReadonlyMap; readonly now: number; }): WakePlan; /** PURE: latest applied-wake epoch ms per agent, from the loop event journal. */ export declare function priorWakeAtByAgent(events: readonly { readonly type: string; readonly at: string; readonly payload?: unknown; }[]): Map; export interface RelaunchHistory { readonly count: number; readonly latestAt?: number; readonly retryForbidden?: boolean; } /** Durable applied+failed attempt fold. Deferred/skipped events consume nothing. */ export declare function priorActionAttemptsByAgent(events: readonly { readonly type: string; readonly at: string; readonly payload?: unknown; }[], action: "wake" | "request-launch"): Map; /** Shared per-agent wake+launch budget required by the loop relaunch policy. */ export declare function priorRelaunchAttemptsByAgent(events: readonly { readonly type: string; readonly at: string; readonly payload?: unknown; }[]): Map; export type LaunchPlan = { readonly kind: "emit"; readonly host?: string; readonly reason: string; } | { readonly kind: "skip"; readonly reason: string; }; /** * PURE: decide whether to ASK for a launch of a missing enrolled agent. Bounded * by `policy.maxRelaunches` (stop asking after N) + a per-agent cooldown. h2a * ASKS (records in the loop journal / — later — an envelope to a remote); it * NEVER spawns arbitrary agents itself (spec objective-loop §Non-goals). */ export declare function planLaunchTarget(input: { readonly loop: H2AObjectiveLoop; readonly agentId: string; readonly priorCount: number; readonly priorLatestAt?: number; readonly retryForbidden?: boolean; readonly now: number; }): LaunchPlan; /** PURE: prior launch-request count + latest epoch ms per agent, from the journal. */ export declare function priorLaunchByAgent(events: readonly { readonly type: string; readonly at: string; readonly payload?: unknown; }[]): Map; export declare const H2A_RUN_API_VERSION = "h2a.run/v1"; export interface LoopLaunchInvocation { readonly command: string; readonly args: readonly string[]; readonly cwd: string; readonly input: string; } export interface LoopLaunchResult { readonly ok: boolean; readonly detail?: string; readonly retrySafe?: boolean; } export interface LoopLauncher { launch(spec: H2ALoopLaunchSpec): LoopLaunchResult | Promise; } /** * Last action-boundary guard. A persisted launch may target only the controller * workspace or an explicitly declared loop repository. This mirrors h2a_run's * startup-root boundary and prevents a forged/stale loop state from launching * an agent in an arbitrary readable directory. */ export declare function loopLaunchWorkspaceAllowed(loop: H2AObjectiveLoop, spec: H2ALoopLaunchSpec, controllerRoot?: string): boolean; export declare function buildLoopLaunchInvocation(value: H2ALoopLaunchSpec, bin?: string): LoopLaunchInvocation; export declare function executeLoopLaunchWithSpawn(value: H2ALoopLaunchSpec, spawn?: typeof spawnSync): LoopLaunchResult; export type LoopWakeOutcome = "done" | "deferred-human" | "failed"; export interface LoopWakeDriver { drive(request: H2ADriveRequest): LoopWakeOutcome | Promise; } /** Typed local-tmux seam: human deferral and transport failure are distinct. */ export declare function localTmuxLoopWakeDriver(options?: { runtime?: Pick; log?: (line: string) => void; }): LoopWakeDriver; export declare function buildActionSink(opts?: { /** Back-compat boolean seam: false means human-deferred. Prefer wakeDriver. */ driver?: H2ADriver; wakeDriver?: LoopWakeDriver; launcher?: LoopLauncher; /** Workspace boundary captured by the objective-loop controller. */ controllerRoot?: string; }): ActionSink; export {}; //# sourceMappingURL=adapters.d.ts.map