/**
 * Sandbox Registry
 *
 * Manages registered agentic-sandbox instances. Each sandbox registers
 * with `aiwg serve` via POST /api/sandboxes/register and then pushes
 * real-time events over WebSocket at /ws/sandbox/:sandboxId.
 *
 * This is the AIWG side of the bidirectional integration:
 *   - aiwg#731 = registration API (this file)
 *   - sandbox#132 = outbound registration (pushes events here)
 *
 * @issue #731
 * @see #710 — epic
 * @see #732 — HITL relay (consumes hitl.input_required events)
 * @see #733 — operator controls (proxies to sandbox HTTP)
 */
export interface SandboxRegistration {
    /** Unique sandbox ID (assigned on register, session-scoped) */
    id: string;
    /** Display name chosen by sandbox operator */
    name: string;
    /**
     * Stable instance identity (UUIDv7) persisted by the sandbox across restarts.
     * Used for upsert-on-reconnect so the same physical sandbox never creates
     * duplicate entries regardless of how many times it re-registers.
     */
    instanceId?: string;
    /** gRPC endpoint for agent communication */
    grpcEndpoint: string;
    /** WebSocket endpoint for PTY streaming */
    wsEndpoint: string;
    /** HTTP REST API endpoint */
    httpEndpoint: string;
    /** Capabilities this sandbox advertises */
    capabilities: string[];
    /** Sandbox software version */
    version: string;
    /** Auth token for this sandbox (returned at registration) */
    token: string;
    /** When this sandbox_id was first assigned */
    registeredAt: string;
    /** When the most recent registration arrived (updated on every upsert) */
    lastRegisteredAt: string;
    /** Last event received from the sandbox */
    lastEventAt: string;
    /** Whether the event push WebSocket is connected */
    connected: boolean;
    /** When the event push WebSocket last disconnected (undefined if never disconnected) */
    disconnectedAt?: string;
    /** Live agent inventory (updated by sandbox events) */
    agents: Map<string, SandboxAgent>;
    /** Sandbox-level artifact inventory reported at registration time (#906) */
    sandboxInventory?: AgentInventory;
    /** WebSocket protocol capabilities advertised at registration time (#912) */
    wsCapabilities?: SandboxCapabilities;
}
export interface SandboxAgent {
    agentId: string;
    status: 'starting' | 'provisioning' | 'ready' | 'busy' | 'error' | 'disconnected';
    loadout?: string;
    /** Framework list — name, providers, and optional version/content_hash (#910) */
    aiwgFrameworks?: Array<{
        name: string;
        providers: string[];
        version?: string;
        content_hash?: string;
    }>;
    connectedAt?: string;
    lastHeartbeat?: string;
    /** Live session count — incremented/decremented by session.start/session.end events.
     *  Approximate; `sessions` is authoritative when present (drift-resistant). */
    sessionCount?: number;
    /**
     * Authoritative session inventory pushed by the sandbox via the
     * `agent.sessions` event (#1151, sandbox#192). Replaces wholesale on each
     * event so a missed start/end can't desync the count.
     *
     * Undefined when the sandbox is on a build that doesn't emit
     * `agent.sessions` yet — UI should fall back to "no badge" rather than
     * rendering "0 sessions" for unknown state.
     */
    sessions?: SessionInfo[];
    /** Agent/command/skill manifest inventory — populated by agent.inventory_updated events (#906) */
    inventory?: AgentInventory;
    /** Latest metrics snapshot — populated by agent.metrics_updated events (#911) */
    latestMetrics?: AgentMetrics;
    /** Rolling metrics history (last METRICS_HISTORY_MAX samples) — for sparklines (#911) */
    metricsHistory?: AgentMetricsSample[];
    /** Current provisioning step — populated by agent.provisioning_step events (#911) */
    provisioningStep?: ProvisioningStep;
    /** True if provisioning has stalled (no progress for 30s+) (#911) */
    provisioningStalled?: boolean;
    /**
     * Stable agent instance identity — UUIDv7 generated on first start, persisted by the sandbox.
     * Survives restarts, reprovisions, and agentId changes (#917).
     */
    instanceId?: string;
    /**
     * Human-readable stable name assigned by the operator (e.g. "security-01").
     * Persisted in ~/.config/aiwg/sandbox-agents.json (#917).
     */
    logicalName?: string;
}
/**
 * One live session record on an agent. Pushed by the sandbox in the
 * `agent.sessions` event (sandbox#192) as a full inventory replace.
 *
 * Field shape mirrors the dashboard's WS `session_list` reply so the UI
 * can render the same data on either surface.
 */
export interface SessionInfo {
    session_id: string;
    session_name: string;
    session_type: 'interactive' | 'headless' | 'background';
    command: string;
    /** Unix epoch seconds — kept as raw seconds so the consumer can format
     *  it in the local timezone. */
    created_at_secs: number;
    /** True when the sandbox has a screen-state snapshot for this session
     *  available via `GET /api/v1/sessions/:id/screen` (#913). */
    has_screen: boolean;
}
/**
 * Protocol capabilities advertised by the sandbox on registration.
 * Enables AIWG to select the correct WS message path for each sandbox.
 */
export interface SandboxCapabilities {
    /** Numeric protocol version (e.g. 2) */
    ws_protocol_version: number;
    /** Client message types this sandbox accepts */
    supported_client_messages: string[];
    /** Server message types this sandbox emits */
    supported_server_messages: string[];
    /** Named feature flags (e.g. "replay_buffer", "role_control", "seq_tracking") */
    features: string[];
}
/** One record per known agent instance in the persistent store. */
interface AgentIdentityRecord {
    instanceId: string;
    logicalName?: string;
    /** Last known agentId — may change across reprovisions */
    lastAgentId?: string;
    /** Last known sandboxId */
    lastSandboxId?: string;
    lastSeenAt: string;
}
/**
 * Resolve the identity-store path, honoring `roots.sandbox_identity`
 * in `.aiwg/storage.config` when set. Sync read because the sandbox
 * registry constructor is sync and load happens at construction time.
 *
 * Backend support: only `fs` (or absent config) is currently supported
 * for the identity store — non-fs backends would require an async
 * adapter call which doesn't fit the sync constructor. Throws a clear
 * error if the user has configured a non-fs backend for this subsystem.
 *
 * Exported for testing — production callers omit `projectRootOverride`
 * to use `process.cwd()`.
 *
 * @issue #969
 */
export declare function resolveIdentityStorePath(projectRootOverride?: string): string;
/** Convenience helper — returns true if the sandbox advertises a feature flag. */
export declare function sandboxHasFeature(reg: {
    wsCapabilities?: SandboxCapabilities;
}, feature: string): boolean;
/** Returns true if the sandbox supports a given client message type. */
export declare function sandboxSupports(reg: {
    wsCapabilities?: SandboxCapabilities;
}, msg: string): boolean;
export interface AgentMetrics {
    cpu_percent: number;
    memory_used_bytes: number;
    memory_total_bytes: number;
    uptime_seconds: number;
    load_avg_1m?: number;
    disk_used_bytes?: number;
    disk_total_bytes?: number;
    /** Unix timestamp (ms) when metrics were sampled */
    ts: number;
}
/** Compact sample stored in the rolling history ring. */
export interface AgentMetricsSample {
    cpu_percent: number;
    memory_percent: number;
    ts: number;
}
/** Maximum number of samples kept per agent (#911). ~5 min at 5s interval. */
export declare const METRICS_HISTORY_MAX = 60;
export interface ProvisioningStep {
    step: string;
    step_index?: number;
    total_steps?: number;
    elapsed_seconds?: number;
    ts: string;
}
export interface AgentManifestSummary {
    name: string;
    description: string;
    model?: string;
    category: string;
    platform: string;
    /** SHA-256 of agent manifest file — for change detection */
    content_hash: string;
}
export interface CommandManifestSummary {
    name: string;
    description: string;
    platform: string;
    content_hash: string;
}
export interface SkillManifestSummary {
    name: string;
    description: string;
    platform: string;
    content_hash: string;
}
export interface AgentInventory {
    agents: AgentManifestSummary[];
    commands: CommandManifestSummary[];
    skills: SkillManifestSummary[];
    last_updated: string;
}
/**
 * Events pushed from agentic-sandbox to aiwg serve over WebSocket.
 * Matches the protocol defined in aiwg#731 / sandbox#132.
 */
export type SandboxEventType = 'agent.connected' | 'agent.disconnected' | 'agent.provisioning' | 'agent.ready' | 'session.start' | 'session.end' | 'hitl.input_required' | 'hitl.responded' | 'hitl.timed_out' | 'agent.inventory_updated' | 'task.submitted' | 'task.started' | 'task.progressed' | 'task.completed' | 'task.failed' | 'agent.metrics_updated' | 'agent.provisioning_step' | 'agent.provisioning_stalled' | 'framework.update_available' | 'session.screen_updated' | 'agent.sessions' | 'aiwg.log';
export interface SandboxEvent {
    type: SandboxEventType;
    sandboxId: string;
    agentId: string;
    timestamp: string;
    loadout?: string;
    aiwgFrameworks?: Array<{
        name: string;
        providers: string[];
    }>;
    step?: string;
    progress?: unknown;
    sessionId?: string;
    /** PTY/exec command — present on session.start events */
    command?: string;
    /** Exit code — present on session.end events */
    exitCode?: number;
    task?: string;
    hitlId?: string;
    prompt?: string;
    context?: string;
    expiresAt?: string;
    agentInventory?: AgentManifestSummary[];
    commandInventory?: CommandManifestSummary[];
    skillInventory?: SkillManifestSummary[];
    taskId?: string;
    outputChunk?: string;
    taskError?: string;
    metrics?: AgentMetrics;
    stepIndex?: number;
    totalSteps?: number;
    elapsedSeconds?: number;
    stalledForSeconds?: number;
    framework?: string;
    currentVersion?: string;
    availableVersion?: string;
    daysBehind?: number;
    screenHash?: string;
    changedLines?: number[];
    level?: string;
    message?: string;
    /** Stable agent instance UUIDv7 — set on agent.connected events */
    agentInstanceId?: string;
    /** Operator-assigned logical name — set on agent.connected events */
    agentLogicalName?: string;
    /** Full session inventory — set on agent.sessions events. The sandbox
     *  replaces the agent's session list wholesale on each event so missed
     *  session.start / session.end deltas can't desync the count. */
    sessions?: SessionInfo[];
}
export interface HitlRequest {
    id: string;
    sandboxId: string;
    agentId: string;
    sessionId: string;
    timestamp: string;
    prompt: string;
    context: string;
    expiresAt?: string;
}
export interface RegisterRequest {
    name: string;
    /** Stable UUIDv7 generated on first start, persisted across restarts */
    instance_id?: string;
    grpc_endpoint: string;
    ws_endpoint: string;
    http_endpoint: string;
    capabilities?: string[];
    version?: string;
    /** Agent manifest summaries for all deployed agents (#906) */
    agent_inventory?: AgentManifestSummary[];
    /** Command manifest summaries for all deployed commands (#906) */
    command_inventory?: CommandManifestSummary[];
    /** Skill manifest summaries for all deployed skills (#906) */
    skill_inventory?: SkillManifestSummary[];
    /** WebSocket protocol capabilities — enables negotiation on connect (#912) */
    ws_capabilities?: SandboxCapabilities;
}
export interface RegisterResponse {
    sandbox_id: string;
    token: string;
}
/**
 * Normalize a raw sandbox event payload into the camelCase + dot-notation
 * shape `handleEvent` expects. Accepts payloads in either the legacy
 * snake_case or the newer dot/camelCase shape and is idempotent on the
 * latter, so it is safe to pipe every inbound event through this helper.
 */
export declare function normalizeSandboxEvent(raw: unknown): SandboxEvent;
export declare class SandboxRegistry {
    private sandboxes;
    private hitlRequests;
    private listeners;
    /** instance_id → sandbox_id (stable reverse-lookup for sandbox upsert) */
    private byInstanceId;
    /** instance_id → last registration timestamp (ms, for debounce) */
    private lastRegistrationTime;
    /** Timer for HITL expiry checks (#908) */
    private expiryTimer;
    /** agentInstanceId → { sandboxId, agentId } — cross-restart lookup (#917) */
    private byAgentInstanceId;
    /** logicalName → { sandboxId, agentId } — human-readable alias lookup (#917) */
    private byLogicalName;
    /** Persistent store: agentInstanceId → identity record (#917) */
    private identityStore;
    constructor();
    private checkHitlExpiry;
    /**
     * Register a sandbox instance.
     *
     * When the request includes a stable `instance_id`:
     *   - **Debounce**: if a registration for the same instance_id arrived within
     *     DEBOUNCE_MS, return the existing sandbox_id + token without touching state.
     *   - **Upsert**: if outside the debounce window, update the existing entry's
     *     endpoints, version, and lastRegisteredAt in-place. The sandbox_id and token
     *     are preserved so in-flight WS connections stay authenticated.
     *
     * When no instance_id is provided, a new entry is always created (legacy behaviour).
     */
    register(req: RegisterRequest): RegisterResponse;
    /**
     * Remove all disconnected sandboxes from the registry.
     * Forces re-registration on next sandbox startup.
     * Returns the number of entries removed.
     */
    clearOffline(): number;
    /**
     * Deregister a sandbox (on shutdown or explicit delete).
     */
    deregister(id: string): boolean;
    /**
     * Get a sandbox by ID.
     */
    get(id: string): SandboxRegistration | undefined;
    /**
     * Validate the auth token for a sandbox.
     */
    authenticate(id: string, token: string): boolean;
    /**
     * Mark the event push WebSocket as connected/disconnected.
     */
    setConnected(id: string, connected: boolean): void;
    /**
     * List all registered sandboxes (serializable).
     */
    list(): SandboxSummary[];
    /**
     * Get a sandbox summary by ID (serializable).
     */
    getSummary(id: string): SandboxSummary | undefined;
    /**
     * Process an event pushed from a sandbox.
     * Updates internal agent inventory and notifies listeners.
     */
    handleEvent(event: SandboxEvent): void;
    /**
     * Emit a synthetic event to all listeners without modifying internal state.
     * Used by serve.ts to push server-side events (e.g. hitl.responded) to the browser.
     */
    emit(event: SandboxEvent): void;
    /**
     * Get all agents across all sandboxes.
     */
    allAgents(): Array<SandboxAgent & {
        sandboxId: string;
        sandboxName: string;
    }>;
    /**
     * Resolve an agent reference by agentId → instanceId → logicalName (#917).
     * Returns the sandbox registration and agent, or undefined if not found.
     */
    resolveAgent(ref: string): {
        sandbox: SandboxRegistration;
        agent: SandboxAgent;
    } | undefined;
    /**
     * Assign a stable logical name to an agent (#917).
     * Persists the mapping to ~/.config/aiwg/sandbox-agents.json.
     */
    aliasAgent(sandboxId: string, agentId: string, logicalName: string): boolean;
    /**
     * List known agent identities from the persistent store (#917).
     */
    knownAgentIdentities(): AgentIdentityRecord[];
    /**
     * List pending HITL requests.
     */
    pendingHitl(): HitlRequest[];
    /**
     * Get a specific HITL request.
     */
    getHitl(hitlId: string): HitlRequest | undefined;
    /**
     * Remove a HITL request (after response or dismissal).
     */
    resolveHitl(hitlId: string): HitlRequest | undefined;
    /**
     * Subscribe to sandbox events (returns unsubscribe fn).
     */
    subscribe(listener: (event: SandboxEvent) => void): () => void;
    /**
     * Total registered sandbox count.
     */
    get size(): number;
    /**
     * Shut down — clear all state and stop background timers.
     */
    shutdown(): void;
}
export interface SandboxSummary {
    id: string;
    /** Stable instance identity — canonical UI identifier, prefix for display */
    instanceId?: string;
    name: string;
    grpcEndpoint: string;
    wsEndpoint: string;
    httpEndpoint: string;
    capabilities: string[];
    version: string;
    registeredAt: string;
    lastRegisteredAt: string;
    lastEventAt: string;
    connected: boolean;
    /** ISO timestamp of last disconnect — present when connected is false */
    disconnectedAt?: string;
    agentCount: number;
    agents: Array<SandboxAgent>;
    /** Sandbox-level artifact inventory reported at registration time (#906) */
    sandboxInventory?: AgentInventory;
    /** WebSocket protocol capabilities advertised at registration (#912) */
    wsCapabilities?: SandboxCapabilities;
}
export declare const sandboxRegistry: SandboxRegistry;
export {};
//# sourceMappingURL=sandbox-registry.d.ts.map