import type { Logger, Role, Channel } from './types.js';
export interface SessionSidecar {
    senderId: string;
    role: Role;
    channel: Channel;
    /** Operator-facing iframe URL from the `/remote-control is active` banner.
     *  Null until url-capture matches; may stay null forever on headless
     *  spawns (no banner) and on archived rows whose live PTY exited before
     *  the URL landed. */
    url: string | null;
    /** ISO 8601 timestamp from `/spawn`. */
    startedAt: string;
    /** Spawn-requested permission mode (e.g. 'default', 'plan', etc.). Null
     *  when not set. This field carries what the operator asked for at
     *  spawn time. The binary's actual runtime mode lives in
     *  `effectivePermissionMode` below. */
    permissionMode: string | null;
    /** Task 278 — binary's actual runtime permission mode, as observed via
     *  the `--debug=auto-mode` capture in `<sessionId>.permission-mode.log`.
     *  Null at spawn until the first `[auto-mode] verifyAutoModeGateAccess`
     *  line resolves. Stays null forever on non-auto spawns (no signal
     *  source for `acceptEdits` / `plan` / `bypassPermissions` on 2.1.150).
     *  When non-null and `!== permissionMode`, the SessionMetadataPane
     *  renders a downgrade warning. */
    effectivePermissionMode: string | null;
    /** Spawn-requested model (e.g. 'claude-opus-4-7'). Null when not set.
     *  Note: the wire's `model` field continues to derive from the JSONL
     *  tail's last assistant turn — this sidecar field records the SPAWN
     *  REQUEST, not the running model, and exists for self-diagnosis. */
    model: string | null;
    /** Hidden spawn — operator sidebar suppresses the row. Known at /spawn
     *  body parse; never changes for a row's lifetime. */
    hidden: boolean;
    /** Specialist agent name from `/spawn` body or resolved agent at PTY
     *  ready. Null for operator (non-specialist) spawns. */
    specialist: string | null;
    /** Task 451 — the systemd transient scope unit token under which this
     *  PTY runs (unit name is `claude-session-<token>.scope`). Persisted on
     *  disk so boot-seed can reattach a surviving scope to its row after
     *  manager restart, even for fresh spawns where the unit token is a
     *  random UUID that does not match claude's intrinsic sessionId. Null
     *  on pre-Task-451 sidecars and on any non-scope spawn path. */
    scopeUnitToken: string | null;
    /** Task 550 — composer bridge ULIDs (suffix only, no "session_" prefix)
     *  that have been bound to this conversation across its lifetime. Every
     *  `/rc-spawn` resume against an existing JSONL learns a new bridge ULID
     *  from the PID file once claude registers with the composer; the
     *  http-server appends it here so a future composer-URL → row lookup is
     *  a direct match. Defaults to `[]` on pre-Task-550 sidecars (missing
     *  key). Append-only, duplicates suppressed. */
    bridgeIds: string[];
}
export interface SidecarReadOutcome {
    sidecar: SessionSidecar | null;
    /** Why `sidecar` is null when null. Lets callers and the watcher's
     *  boot-seed log line distinguish "no sidecar on disk" from "sidecar
     *  unparseable" — both yield null fields on the row, but the latter
     *  signals a write defect worth investigating. */
    outcome: 'ok' | 'missing' | 'parse-failed';
    /** File size in bytes when present; 0 otherwise. */
    bytes: number;
}
/** Read a sidecar from disk. Returns `outcome: 'missing'` when the file
 *  is absent; `outcome: 'parse-failed'` when it exists but cannot be
 *  parsed as the expected shape. The caller (watcher row builder) stamps
 *  null fields on the row in both cases and the boot-seed log line
 *  surfaces the mismatch. */
export declare function readSidecar(path: string, logger: Logger): SidecarReadOutcome;
/** Write a sidecar atomically: tmp file in the same directory, then
 *  rename. Throws if the directory cannot be created or the rename
 *  fails — the spawn route translates this to a loud 500 rather than
 *  launching a metadata-less PTY. */
export declare function writeSidecar(path: string, sidecar: SessionSidecar, logger: Logger): void;
/** Read-modify-write update. Returns true when the field was written;
 *  false when the sidecar is missing or unparseable (caller may choose
 *  to log; we don't write a fresh sidecar from a partial update because
 *  the other fields would be invented). Single-writer assumption applies
 *  — concurrent updates may race and lose the latest value. */
export declare function updateSidecar(path: string, patch: Partial<SessionSidecar>, logger: Logger): boolean;
/** Task 550 — append a bridge ULID suffix to `bridgeIds` on the sidecar.
 *  Read-modify-write; the atomic rename pattern matches the rest of this
 *  module. Returns `'ok'` when written, `'duplicate'` when the suffix was
 *  already present (no write performed), and `'no-sidecar'` when the
 *  sidecar is missing or unparseable (caller logs and skips — legacy
 *  rows without a sidecar are out of scope per the task brief).
 *  Single-writer assumption is shared with url-capture's updateSidecar:
 *  both are read-modify-write and could race, but they touch disjoint
 *  fields, so the rename-loses-write window is the existing limitation
 *  rather than a new one. */
export declare function appendBridgeId(path: string, bridgeSuffix: string, logger: Logger): 'ok' | 'duplicate' | 'no-sidecar';
//# sourceMappingURL=session-sidecar.d.ts.map