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; /** Permission mode recorded at spawn time. For `/spawn` this is the * operator's spawn-request choice; for `/rc-spawn` (Task 798) it is the * settings-resolved mode the session actually runs under * (`permissions.defaultMode`, else 'default') — rc-spawn argv carries no * `--permission-mode` and its request has no mode field. Null on * sessions spawned before Task 798. The binary's observed 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 `.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-.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[]; /** Task 863 — public-webchat visitor identity (Neo4j Person elementId) * captured at `/spawn` from the AccessSession. Null for every non-public * spawn and for sessions spawned before Task 863 (missing key). The admin * Sessions panel reads it back to render the visitor's display name. */ personId: string | null; /** Task 865 — admin-webchat author identity: the authenticated admin's * `AdminUser.userId`, threaded from the PIN session at webchat spawn. Null * for every non-admin-webchat session and for pre-865 sidecars (missing * key). Distinct from `personId`: the admin is resolved by the * `AdminUser.userId` property (loadAdminUserName), the public visitor by * Neo4j elementId. Mutually exclusive with `personId` per row (admin vs * public role). */ adminUserId: string | null; /** Task 933 — the resolved authenticated-admin display name, persisted so the * manager (which holds no Neo4j driver) can inject the identity segment on a * caller-driven channel resume, where the request body carries no * `authenticatedName`. Written/refreshed from the rc-spawn body whenever it * carries one. Null for non-admin-webchat sessions and for pre-933 sidecars * (missing key); a null here on an admin-webchat resume yields the explicit * not-resolvable identity form. */ authenticatedName: string | null; /** Task 884 — open-mode public-webchat visitor identity (the `maxy_visitor` * cookie UUID). Null for gated/admin spawns and for pre-884 sidecars. The * public reader matches it as the open-mode ownership anchor (the open twin * of `personId`). */ visitorId: string | null; /** Task 884 — the resolved public agent slug for this session. Stamped on * every public webchat spawn so the open-mode reader can scope a visitor's * session resolution per agent (one shared `maxy_visitor` cookie spans all * open agents on the site). Null for non-public spawns and pre-884 sidecars. */ agentSlug: string | null; /** Task 983 — the account this session is scoped to (ACCOUNT_ID at spawn). The * admin shell's account switcher filters the session list by it. Optional on * disk: pre-983 sidecars (no key) and non-rc-spawn paths load null, which the * reader treats as the house account. */ accountId?: string | null; /** Task 1440 — the HOUSE_ADMIN_SCOPE value stamped into this session's spawn * env, or null when the session carries no cross-account authority. The * cross-account census reads it as the independent record of the spawn-time * scope decision: `houseAdminScope != null` on a session whose account is no * longer role:"house" is the escalation-precursor violation. Optional on * disk; pre-1440 sidecars (no key) load null. */ houseAdminScope?: string | null; } 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, 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