/**
 * DaemonCommandRouter — Unified command routing for daemon-level commands
 *
 * Unified command routing for daemon-level commands.
 *
 * Routing flow:
 *   1. Daemon-level commands (launch_ide, stop_ide, restart_ide, etc.) → handled here
 *   2. CLI/ACP commands → delegated to cliManager
 *   3. Everything else → delegated to commandHandler.handle()
 */
import { DaemonCdpManager } from '../cdp/manager.js';
import { DaemonCommandHandler } from './handler.js';
import { DaemonCliManager } from './cli-manager.js';
import type { ProviderLoader } from '../providers/provider-loader.js';
import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
import { SessionRegistry } from '../sessions/registry.js';
import type { RepoMeshSessionCleanupMode, RepoMeshSpawnedSessionVisibility } from '../repo-mesh-types.js';
import { MeshRefineBatchJobHandle, MeshRefineBatchTerminalJob, MeshRefineJobHandle, MeshRefineTerminalJob } from '../mesh/mesh-refine-gates.js';
export * from '../mesh/mesh-node-identity.js';
export * from '../mesh/mesh-refine-gates.js';
export * from '../mesh/mesh-coordinator-config.js';
export interface SessionHostControlPlane {
    getDiagnostics(payload?: {
        includeSessions?: boolean;
        limit?: number;
    }): Promise<any>;
    listSessions(): Promise<any[]>;
    stopSession(sessionId: string): Promise<any>;
    deleteSession(sessionId: string, opts?: {
        force?: boolean;
    }): Promise<any>;
    resumeSession(sessionId: string): Promise<any>;
    restartSession(sessionId: string): Promise<any>;
    sendSignal(sessionId: string, signal: string): Promise<any>;
    forceDetachClient(sessionId: string, clientId: string): Promise<any>;
    pruneDuplicateSessions(payload?: {
        providerType?: string;
        workspace?: string;
        dryRun?: boolean;
    }): Promise<any>;
    acquireWrite(payload: {
        sessionId: string;
        clientId: string;
        ownerType: 'agent' | 'user';
        force?: boolean;
    }): Promise<any>;
    releaseWrite(payload: {
        sessionId: string;
        clientId: string;
    }): Promise<any>;
}
export interface CommandRouterDeps {
    commandHandler: DaemonCommandHandler;
    cliManager: DaemonCliManager;
    cdpManagers: Map<string, DaemonCdpManager>;
    providerLoader: ProviderLoader;
    instanceManager: ProviderInstanceManager;
    /** Reference to detected IDEs array (mutable — router updates it) */
    detectedIdes: {
        value: any[];
    };
    sessionRegistry: SessionRegistry;
    /** Callback after CDP manager created (transport-specific extras) */
    onCdpManagerCreated?: (ideType: string, manager: DaemonCdpManager) => void;
    /** Callback after IDE connected (e.g., startAgentStreamPolling) */
    onIdeConnected?: () => void;
    /** Callback after status change (stop_ide, restart) */
    onStatusChange?: () => void;
    /** Callback when a mesh state is invalidated */
    onMeshStateChange?: (meshId: string) => void;
    /** Callback after chat-related commands */
    onPostChatCommand?: () => void;
    /** Get a connected CDP manager (for agent stream reset check) */
    getCdpLogFn?: (ideType: string) => (msg: string) => void;
    /** Package name for upgrade detection ('adhdev' or '@adhdev/daemon-standalone') */
    packageName?: string;
    /** Canonical daemon status identity used by snapshot commands */
    statusInstanceId?: string;
    statusVersion?: string;
    /** Session host control plane */
    sessionHostControl?: SessionHostControlPlane | null;
    /** Selected-coordinator mesh peer telemetry surface for target daemons, when supported by the runtime. */
    getMeshPeerConnectionStatus?: (daemonId: string) => Record<string, unknown> | null;
    /** Dispatch a command to a remote mesh node via P2P/relay. Injected by cloud runtime; absent in standalone. */
    dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
    /**
     * Refresh THIS daemon's own coordinator mirror (adhdev-daemon meshOwnedSessions) for a
     * self-hosted mesh session, applying the same `mesh_forward_event`-shaped payload the remote
     * relay would carry — used by the SELF-DIAL branch of set_conversation_prefs so a locally
     * coordinated session (coordinatorDaemonId == this daemon) updates its mirror directly instead
     * of dispatching a P2P command to its own id (which the mesh manager refuses as SELF_DIAL).
     * Injected by the cloud runtime (calls updateMeshOwnedSession + flushes the dashboard
     * subscription); absent in standalone (no coordinator mirror there).
     */
    updateLocalMeshOwnedSession?: (payload: Record<string, unknown>) => void;
}
export interface CommandRouterResult {
    success: boolean;
    [key: string]: unknown;
}
/**
 * Confine a spec path to ~/.adhdev/providers, defeating both prefix-bypass
 * (e.g. ".../providers-evil") and symlink escape. Resolves the real path of
 * the *parent* directory (the file may not exist yet for writes), requires the
 * basename to be a literal `*.json`, and re-joins under the verified parent so
 * the returned path can't point outside the tree. Used by get/write_spec_source.
 */
export declare function normalizeStandaloneHostCommandUrl(hostAddress: string): string;
export declare function buildMemberJoinNode(mesh: any, args: any, fallbackDaemonId?: string): Record<string, unknown> | null;
export declare class DaemonCommandRouter {
    /** Public (not private) so the extracted ./router-refine.ts orchestration can reach it via `self`. */
    deps: CommandRouterDeps;
    /** In-memory cache for cloud-originating meshes passed via inlineMesh.
     *  Allows the MCP server to query mesh data via get_mesh even when
     *  the mesh doesn't exist in the local meshes.json file. */
    private inlineMeshCache;
    /** Tombstones for inline mesh nodes removed via remove_mesh_node, keyed by
     *  meshId → set of removed nodeIds. The dashboard keeps echoing the removed
     *  node in the inlineMesh it attaches to every command; without a tombstone,
     *  reconcileInlineMeshCache MERGEs it straight back (resurrection). A
     *  tombstoned node is skipped during reconcile only while its workspace is
     *  absent from disk — a genuine re-registration (same nodeId, workspace back
     *  on disk) clears the tombstone and merges normally, preserving clone
     *  worktree visibility and legitimate node re-creation. */
    private removedInlineMeshNodeIds;
    /** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default.
     *  Public (not private) so the extracted ./router-aggregate-status.ts orchestration can reach it via `self`. */
    aggregateMeshStatusCache: Map<string, {
        builtAt: number;
        snapshot: any;
        queueRevision: string;
    }>;
    /** Shared per-peer git_status probe dedup + recently-probed reuse gate.
     *  Spans separate mesh_status/get_mesh calls so the dashboard auto-retry
     *  loop cannot storm a slow peer with back-to-back refreshUpstream probes. */
    private meshGitProbeCache;
    /** Meshes with a background SWR freshen (async mesh_status refresh) already in
     *  flight — so a burst of interactive detail-opens serves the cached snapshot
     *  and coalesces onto ONE background refresh instead of storming the peers. */
    private swrRefreshInFlight;
    /** In-memory async Refinery jobs keyed by meshId:nodeId to reject/return duplicate in-flight requests.
     *  Public (not private) so the extracted ./router-refine.ts orchestration can reach it via `self`. */
    runningRefineJobs: Map<string, MeshRefineJobHandle>;
    /** Terminal async Refinery jobs preserve a clear answer after the worktree node has been removed. */
    terminalRefineJobs: Map<string, MeshRefineTerminalJob>;
    /** In-memory async batch Refinery jobs keyed by meshId (one batch convergence per mesh at a time). */
    runningRefineBatchJobs: Map<string, MeshRefineBatchJobHandle>;
    /** Terminal async batch Refinery jobs preserve the last batch outcome for late readers. */
    terminalRefineBatchJobs: Map<string, MeshRefineBatchTerminalJob>;
    /**
     * DS2: in-process refinement leases keyed by `${repoRoot}::${baseBranch}`. Serialize
     * the base-mutating window (candidate-SHA pin → merge → push) of concurrent single-node
     * refines that target the SAME base branch in the same repo, so two refines cannot both
     * validate against one baseHead and then race their merges (the base-movement race). The
     * batch path is already sequential, so this only matters for overlapping single-node
     * async jobs. Value = the meshId:nodeId job key holding the lease (for diagnostics).
     */
    refineBaseLeases: Map<string, string>;
    constructor(deps: CommandRouterDeps);
    private hydrateCachedAggregateMeshStatusFromInline;
    private getCachedAggregateMeshStatus;
    private rememberAggregateMeshStatus;
    getCachedInlineMeshNodes(): any[];
    /**
     * Same flattened node list as getCachedInlineMeshNodes(), but each node is
     * paired with the `spawnedSessionVisibility` from its OWNING mesh's policy.
     * The flat node list loses the mesh→node association, yet the cloud daemon's
     * synthetic mesh-session mirror needs the mesh-level visibility policy to
     * decide whether a coordinator-spawned worker session should be hidden+muted
     * on the dashboard (the cached inline-mesh session entry carries no settings
     * of its own). Falls back to DEFAULT_MESH_POLICY.spawnedSessionVisibility when
     * the mesh policy is absent, matching the worker-launch stamp.
     */
    getCachedInlineMeshNodesWithVisibility(): Array<{
        node: any;
        spawnedSessionVisibility: RepoMeshSpawnedSessionVisibility;
    }>;
    resolveRemoteMeshSessionOwnerDaemonId(sessionId: string, ownerNodeIdHint?: string): string | undefined;
    getCachedInlineMesh(meshId: string, inlineMesh?: unknown): any | undefined;
    private warmInlineMeshCache;
    getMeshForCommand(meshId: string, inlineMesh?: unknown, options?: {
        preferInline?: boolean;
    }): Promise<{
        mesh: any;
        inline: boolean;
        source: 'inline_cache' | 'inline_bootstrap' | 'local_config';
    } | null>;
    invalidateAggregateMeshStatus(meshId: string): void;
    /**
     * Build the MedFamilyContext handed to RF-ROUTER MED family handlers. Binds the
     * router-private collaborators those handlers need (mesh resolution, owner
     * gating, inline-cache mutation, worktree / session cleanup, refine job
     * starters, IDE stop/launch) plus the inline-mesh and git-probe caches. The
     * `launchIde` field closes over the freshly-built context so restart_session /
     * restart_ide invoke the IDE launch directly instead of recursing through
     * executeDaemonCommand('launch_ide').
     */
    private buildMedFamilyContext;
    /**
     * Build the HighFamilyContext handed to RF-ROUTER HIGH family handlers. Binds
     * the router-private collaborators those handlers need (mesh resolution, the
     * aggregate-status memory cache + its bound read/write helpers, the
     * running-refine-job table, inline-mesh + git-probe caches, and the router's
     * own `execute` for the get_mesh_review_inbox mesh_status re-entry). HIGH
     * handlers reach more router-owned state than MED, but the binding shape is
     * the same: bound methods + direct field references, none reachable from
     * `deps`.
     */
    private buildHighFamilyContext;
    private requireMeshHostMutationOwner;
    private updateInlineMeshNode;
    private removeInlineMeshNode;
    /**
     * WORKTREE-BOOTSTRAP-COORD-STATE: mark a worktree node's bootstrap as reaching a
     * terminal state (complete / failed) in THIS daemon's mesh view.
     *
     * Root cause this fixes: clone_mesh_node forwards the clone+bootstrap to the
     * source node's daemon (the worktree's machine). persistWorktreeSetupState
     * therefore flips worktreeBootstrap.status to 'complete' on the WORKER daemon's
     * mesh object — never on the coordinator's. The coordinator only ever holds the
     * 'running' state it stamped from the forwarded clone reply. The claim path's
     * bootstrap gate (mesh-event-forwarding agent:ready / mesh-queue-assignment)
     * reads the coordinator's mesh via getMeshWithCache, sees status==='running'
     * forever, and DEFERS every claim — so the worktree_bootstrap_complete re-fire
     * (triggerMeshQueue) loops against a gate that never opens: claim never lands,
     * the idle session is re-registered each tick, and auto-launch keeps spawning
     * fresh sessions (runaway worktree-session multiplication).
     *
     * Called from the worktree_bootstrap_complete/_failed event handler BEFORE the
     * queue re-fire so the gate sees the terminal state and the deferred claim can
     * finally land. Updates the inline cache (clone worktree nodes are inline-only)
     * and, when the node also exists in local config, persists there too; both paths
     * invalidate the aggregate status cache. Best-effort and idempotent.
     */
    markWorktreeBootstrapTerminalState(meshId: string, nodeId: string, status: 'complete' | 'failed', opts?: {
        workspace?: string;
    }): void;
    private tombstoneRemovedInlineMeshNode;
    /** Filter an incoming inline mesh against this mesh's tombstones before it is
     *  reconciled into the cache. A tombstoned node is dropped only while its
     *  workspace is still absent from disk; if the workspace is back (genuine
     *  re-registration), the tombstone is cleared and the node merges normally. */
    private applyInlineMeshNodeTombstones;
    normalizeMeshSessionCleanupMode(value: unknown): RepoMeshSessionCleanupMode;
    sessionMatchesMeshNode(record: any, node: any, nodeId: string, sessionIds?: Set<string>): boolean;
    bestEffortRemoveWorktreeDir(dir: string): Promise<{
        removed: boolean;
        residue: boolean;
        error?: string;
    }>;
    precheckLocalWorktreeRemovable(args: {
        mesh: any;
        node: any;
        nodeId: string;
        force?: boolean;
    }): Promise<{
        ok: true;
    } | {
        ok: false;
        code: string;
        error: string;
        recoveryHint: string;
    }>;
    cleanupLocalWorktreeNode(args: {
        mesh: any;
        node: any;
        nodeId: string;
        force?: boolean;
    }): Promise<{
        success: true;
        skipped?: boolean;
        removedPath?: string;
        repoRoot?: string;
        reason?: string;
        fallback?: string;
        forced?: boolean;
        convergence?: Record<string, unknown>;
        recovered?: boolean;
        residue?: boolean;
        residueWarning?: string;
        residueError?: string;
        branchRefDeleted?: boolean;
        branchRefReason?: string;
        branchRefForced?: boolean;
        branchRefWarning?: string;
    } | {
        success: false;
        code: string;
        error: string;
        recoveryHint: string;
        convergence?: Record<string, unknown>;
    }>;
    getWorktreeForceCleanupConvergence(args: {
        repoRoot: string;
        workspace: string;
        node: any;
    }): Promise<{
        allow: boolean;
        status?: string;
        source?: string;
        ref?: string;
        error?: string;
    }>;
    isCompletedHostedSession(record: any): boolean;
    recordIntentionalMeshSessionStop(args: {
        meshId: string;
        nodeId: string;
        node: any;
        sessionId: string;
        mode: RepoMeshSessionCleanupMode;
        source: 'mesh_cleanup_sessions' | 'mesh_remove_node' | 'magi_session_cleanup';
        action: 'stop_session' | 'delete_session_force';
    }): Promise<void>;
    cleanupMeshSessions(args: {
        meshId: string;
        nodeId: string;
        node: any;
        mode: RepoMeshSessionCleanupMode;
        sessionIds?: string[];
        dryRun?: boolean;
        source?: 'mesh_cleanup_sessions' | 'mesh_remove_node' | 'magi_session_cleanup';
        requireAutoLaunchedForTaskIds?: Record<string, string>;
        reclaimOrphans?: boolean;
        liveMeshNodeIds?: string[];
    }): Promise<{
        success: boolean;
        [key: string]: unknown;
    }>;
    /**
     * Unified command routing.
     * Returns result for all commands:
     *   1. Daemon-level commands (launch_ide, stop_ide, etc.)
     *   2. CLI commands (launch_cli, stop_cli, agent_command)
     *   3. DaemonCommandHandler delegation (CDP/agent-stream/file commands)
     *
     * @param cmd Command name
     * @param args Command arguments
     * @param source Log source ('ws' | 'p2p' | 'standalone' | etc.)
     */
    execute(cmd: string, args: any, source?: string): Promise<CommandRouterResult>;
    resumePendingRefineJobsOnStartup(): Promise<void>;
    private batchRefineMeshNodes;
    private startMeshRefineBatchJob;
    private startMeshRefineJob;
    /**
     * Daemon-level command execution (IDE start/stop/restart, CLI, detect, logs).
     * Returns null if not handled at this level → caller delegates to CommandHandler.
     */
    private executeDaemonCommand;
    /**
     * IDE stop: CDP disconnect + InstanceManager cleanup + optionally kill OS process
     */
    private stopIde;
}
