import type { Logger, PtyHandle } from './types.js';
import type { ToolSurface } from './tool-surface.js';
export interface RcDaemonDeps {
    claudeBin: string;
    spawnCwd: string;
    accountDir: string;
    accountId: string;
    /** Admin operator's userId, sourced from users.json at boot. Optional —
     *  absent on pre-onboarding installs where users.json does not yet exist.
     *  Stamped as USER_ID in the child env so MCP plugins that key reads by
     *  (accountId, userId) — e.g. scheduling — receive a non-empty value. */
    userId?: string;
    /** brand.json.productName ("Maxy", "Real Agent"). Passed as the
     *  `--remote-control-session-name-prefix` so composer sessions render as
     *  `<productName> · <id>` instead of the OS hostname default. */
    brandProductName: string;
    claudeConfigDir: string;
    platformRoot: string;
    logDir: string;
    brandedNeo4jUri: string;
    brandedNeo4jPassword: string;
    /** MAXY_UI_INTERNAL_PORT — the loopback port the Stop hook posts to. */
    uiPort: string;
    toolSurface: ToolSurface;
    logger: Logger;
    /** Task 573 — node-pty PTY spawn primitive shared with rc-spawn. The
     *  manager holds one master fd per live daemon; node-pty allocates a
     *  real TTY for the child, exit detection runs on the pty's `onExit`,
     *  and the fast-exit output tail comes from a bounded ring buffer
     *  filled by `onData`. The earlier non-PTY scope primitive
     *  (Tasks 552/556) is gone — its activation gate misread the scope's
     *  brief `inactive` window as a child crash, and the retained
     *  typescript file was redundant once node-pty exposes the child's
     *  stdout/stderr directly. `index.ts` wires the same `spawnPtyAdapter`
     *  used for `/rc-spawn`. */
    spawnPty: (cmd: string, args: string[], env: NodeJS.ProcessEnv, unitToken: string) => PtyHandle;
    /** Task 573 — resolves once `claude rc` writes its on-disk PID file
     *  at `<CLAUDE_CONFIG_DIR>/sessions/<pid>.json`. Presence of this file
     *  is the success signal for the daemon (rejected = pidfile never
     *  appeared within `pidFileTimeoutMs`). `index.ts` wires
     *  `watcher.waitForPid`. */
    waitForPid: (pid: number, timeoutMs: number) => Promise<{
        pidFilePath: string | null;
    }>;
    /** Timeout for the post-spawn PID-file wait. Matches the rc-spawn
     *  surface's `pidFileTimeoutMs`. */
    pidFileTimeoutMs?: number;
    /** Injectable clock for tests. Defaults to Date.now. */
    now?: () => number;
    /** Injectable timer for tests. Defaults to setTimeout. */
    setTimer?: (fn: () => void, ms: number) => unknown;
    /** Process env source — injectable so tests don't mutate process.env. */
    env?: NodeJS.ProcessEnv;
    /** Neo4j readiness probe. Returns true once Neo4j is accepting TCP
     *  connections on the bolt port parsed from `brandedNeo4jUri`. Defaults to
     *  a `node:net` connect with a 5s timeout. Injectable for tests. A thrown
     *  rejection is treated as not-ready, never an unhandled rejection. */
    checkNeo4jReady?: (uri: string) => Promise<boolean>;
    /** Interval between readiness probes. Default 2000ms. */
    neo4jReadyPollIntervalMs?: number;
    /** Bounded readiness wait. After this elapses with Neo4j never reachable,
     *  the daemon permanently-fails (distinct log, no spawn) instead of waiting
     *  forever. Default 300000ms (5 min) — comfortably exceeds a real Neo4j
     *  cold-start after a host reboot. */
    neo4jReadyTimeoutMs?: number;
}
export interface RcDaemon {
    stop(): void;
    /** Task 562 — count of currently-live daemon scope handles (0 or 1).
     *  Exposed for the rc-life census + shutdown audit so the shared
     *  observability schema can report a daemon's lifecycle alongside
     *  the script-scope rc-spawn tracker. */
    alive(): number;
}
export declare const RC_DAEMON_CAPACITY = 10;
/** Parse the bolt host:port from a Neo4j URI (`bolt://`, `neo4j://`,
 *  `bolt+s://`, …). Returns null when no explicit `host:port` is present —
 *  the caller then skips the readiness gate rather than probing a guessed
 *  port, which on a non-default brand could falsely pass against another
 *  brand's Neo4j (brand isolation is absolute). */
export declare function parseBoltHostPort(uri: string): {
    host: string;
    port: number;
} | null;
/** Read account.json.adminModel. Returns null when the file is absent,
 *  unparseable, or the field is empty — the daemon still starts (the
 *  composer falls back to claude's own default model) but the
 *  inception-model lever is logged as unset. */
export declare function readAdminModel(accountDir: string): string | null;
/** Task 578 — pre-seed RC consent. `claude rc` prompts
 *  `Enable Remote Control? (y/n)` on first run; the daemon spawn is
 *  headless so the prompt hangs and the supervisor restarts the child
 *  until it permanently-fails. Writing `remoteControlAtStartup: true`
 *  into `$CLAUDE_CONFIG_DIR/.claude.json` is the same mechanism
 *  claude itself uses when the operator answers `y`, so the prompt is
 *  never reached on subsequent spawns either. Read-merge-write owns
 *  only this one key; siblings (`teammateMode`, `hasUsedRemoteControl`,
 *  `remoteControlUpsellSeenCount`, claude's own auth blocks) are
 *  preserved. Atomic write mirrors `mergeSettingsModel`. */
export declare function ensureRemoteControlConsent(claudeConfigDir: string, logger: Logger): void;
/** Task 578 — substring scan over a `claude rc` PTY output chunk that
 *  returns true once the daemon has reached its post-bind handshake.
 *  Two markers, both emitted by the daemon's own output after RC
 *  registers: the literal "Capacity:" header, and a capacity line of
 *  the form "<id> · <prefix> · N of M" the composer surface prints
 *  (operator-observed `1 of 32`). The `· N of M` form is preceded by
 *  the `·` separator the composer surface uses; matching on `· N of M`
 *  rejects unrelated prose like "retry 1 of 3" that happens to share
 *  the N-of-M shape. A chunk that contains neither marker is pre-bind
 *  output (boot banner, consent prompt, version line) and the flag
 *  stays false. Pure function so it can be unit-tested without a real
 *  PTY. */
export declare function detectRcHandshake(chunk: string): boolean;
/** Read-merge-write settings.json so only the `model` key is owned. Every
 *  other key (hooks, permissions, env, statusLine, …) is preserved verbatim
 *  — the file is shared with maxy-spawned sessions and carries the installed
 *  Stop hook. A missing/unparseable file starts from an empty object. */
export declare function mergeSettingsModel(claudeConfigDir: string, adminModel: string, logger: Logger): void;
/** Fields the rc-daemon child env and any sibling fire-and-forget rc spawn
 *  (Task 543 — sessions-pane resume + new-session) share. Both inherit the
 *  same env shape so the installed admin Stop hook fires
 *  (MAXY_SESSION_ROLE, MAXY_UI_INTERNAL_PORT, ACCOUNT_ID) and the graph
 *  pipeline reaches Neo4j. */
export interface RcChildEnvParts {
    accountId: string;
    userId?: string;
    /** Per-session conversation id (Task 606). Threaded by the `/rc-spawn`
     *  route so session-linked writes (schedule-event PART_OF, memory
     *  createdBySession) resolve `process.env.SESSION_ID`. Absent on the
     *  shared long-lived daemon (it is not a conversation) and on the rare
     *  CLI fresh-spawn with no requested id — both yield SESSION_ID=''. */
    sessionId?: string;
    uiPort: string;
    platformRoot: string;
    claudeConfigDir: string;
    brandedNeo4jUri: string;
    brandedNeo4jPassword: string;
    /** Brand log dir (Task 606). Carried so the MCP `mcp-stderr-tee` writes
     *  its readable per-server `mcp-<name>-stderr-<date>.log`; dropped from the
     *  rc child env at the Task 500 migration, which masked the boot signal. */
    logDir: string;
    /** Task 626 — public session-end review runs on `/rc-spawn`. When set, the
     *  child (and the `database-operator` it dispatches via Task) stays inside
     *  the visitor's slice ringfence, mirroring the spawnClaudeSession reviewer
     *  env: `memory-write` stamps sliceToken + scope=user, `memory-search`
     *  restricts to the slice + public scope. Absent on the daemon and on
     *  channel-admin / LinkedIn rc spawns. */
    sliceToken?: string;
    personId?: string;
    env?: NodeJS.ProcessEnv;
}
/** Compose the env carried by the rc-daemon child and by Task 543's
 *  sessions-pane spawn route. Exported so the route's HTTP handler can
 *  build a byte-identical env without duplicating the field set. */
export declare function buildRcChildEnv(parts: RcChildEnvParts): NodeJS.ProcessEnv;
export declare function startRcDaemon(deps: RcDaemonDeps): RcDaemon;
//# sourceMappingURL=rc-daemon.d.ts.map