import { type ProfileClient } from "./profile-client.js"; import { SkillSelectionError, type SelectionCacheOptions } from "./selection-cache.js"; export interface SessionReconciliationInput { sessionId: string; fromProfile: string; fromRevision: string; receiptSha256: string; selectionProfile: string; profileRevision: string; apply?: boolean; planDigest?: string; planIssuedAt?: string; planExpiresAt?: string; } export interface SessionReconciliationOptions extends SelectionCacheOptions { client?: ProfileClient; } export declare const SESSION_RECONCILIATION_PLAN_TTL_MS: number; /** Exact bundle identity only; never spread receipt, policy or payload fields. */ export interface SessionSelectionMetadata { slug: string; version: string; bundleDigest: string; } export interface SessionSelectionDelta { selection: SessionSelectionMetadata; loaded: boolean; outcome: "retained" | "retired" | "unloaded"; reason: "same-bundle" | "selection-removed" | "bundle-changed"; replacement?: SessionSelectionMetadata; } /** Metadata only: inspection never displays skill payloads or resolves credentials. */ export declare function inspectSkillSession(sessionId: string, options?: SelectionCacheOptions): { sessionId: string; path: string; receiptSha256: string; generation: number; verifiedAt: string; authority: string; workspaceId: string; profileId: string; profileRevision: string; selectionCount: number; loadedCount: number; selections: { loaded: boolean; slug: string; version: string; bundleDigest: string; }[]; }; /** The managed user-prompt hook's own deadline, measured from hook start. */ export declare const MANAGED_HOOK_DEADLINE_MS = 12000; /** * The managed context child's own ceiling. It stays above * SESSION_RENEWAL_TIMEOUT_MS so the parent never kills a renewal that is still * inside its own budget, and below MANAGED_HOOK_DEADLINE_MS. */ export declare const SESSION_CONTEXT_CHILD_CEILING_MS = 11000; /** * Kept by the managed hook after its context child's limit: the hook's own * startup before it set its deadline (an outer supervisor's timer was already * running) and writing its diagnostic, so the hook reports before it is killed. */ export declare const MANAGED_HOOK_EXIT_RESERVE_MS = 1000; /** * Kept by the context child around its renewal: its own startup before the * renewal deadline starts and writing its result. A renewal at a 9 s budget * measured about 9.25 s of child time in cli.session-renewal-budget.test.ts. */ export declare const SESSION_CONTEXT_CHILD_RESERVE_MS = 1000; /** * SessionStart refreshes the station profile before its context. That * refresh is bounded so it cannot use the window verified context needs: it * ends by this budget, and always leaves SESSION_START_CONTEXT_RESERVE_MS of * the hook's window (the context child's startup, a background renewal's * bounded wait and the hook's report). A refresh that times out or cannot * reach the authority is reported in one line, and the session uses its last * verified profile within the usual 24-hour bound. */ export declare const SESSION_START_SYNC_BUDGET_MS = 5000; export declare const SESSION_START_CONTEXT_RESERVE_MS = 6000; export declare function sessionStartSyncBudget(remainingMs: number): number; /** * Limits for one managed context child, from the time the hook has left. On a * full prompt window the renewal keeps its whole budget; after a lifecycle * sync has used part of the window, the renewal shrinks first, so each layer * times out inside the one that encloses it and reports its own code. */ export declare function managedContextChildLimits(remainingMs: number): { childMs: number; renewalBudgetMs: number; }; /** * Renewal budget for one managed-hook prompt. A renewal re-reads every changed * pinned selection at SESSION_RENEWAL_CONCURRENCY, so this has to cover the * profile's real churn and not just the profile read. Measured on the live * 831-selection profile with a pin carrying 807 selections, 85 of them changed: * the exact-version reads alone took ~6.1 s cold and ~4.6 s warm, before the * client, the two profile resolves and the receipt write. A 4-second budget sat * inside that range, so one pin renewed or timed out on authority latency alone. */ export declare const SESSION_RENEWAL_TIMEOUT_MS = 9000; /** * Budget of one background renewal (`skills sessions renew-background`), * which managed hooks start instead of renewing on the prompt's critical * path. It covers a large pin's historical reads on a slow authority; the * authorization checks are the inline renewal's own. */ export declare const BACKGROUND_SESSION_RENEWAL_BUDGET_MS = 120000; /** * A root session's pin is renewed ahead of expiry once it is this old, while * it is still valid, so an active session normally never reaches expiry and * its prompts keep receiving context from the valid pin meanwhile. */ export declare const SESSION_RENEW_AHEAD_AGE_MS: number; /** * A definitive automatic renewal refusal is remembered briefly so the managed * hook does not resolve the whole profile again on every prompt. The record * only ever repeats that refusal: it never authorizes, renews or writes a pin. * It binds the exact receipt bytes, profile, authority, workspace and the * locally synced profile revision, and lapses after a fixed window that the * file cannot extend. Anything unexpected is ignored and the authority is asked. * * Two kinds exist. A renewal refusal says only that this pin cannot be renewed * as it is, so it is repeated only while migration is off. A refusal of the * managed hook's migration itself (a project lock, a rotated epoch, a planner * refusal or anything else that is not transient) also implies the renewal * refusal, so it is repeated in either mode. Timeouts, an unavailable * authority, write-lock contention and other transient failures are never * remembered. */ export declare const SESSION_RENEWAL_REFUSAL_TTL_MS: number; /** * Return the pending migration notice for this session once its pin holds the * migrated profile, then mark it delivered. Anything unexpected returns null. */ export declare function takeSessionPinMigrationNotice(sessionId: string, options?: SelectionCacheOptions): string | null; /** * Renew authorization for the complete immutable pin; never manufacture a * profile combining historical loaded versions with current unloaded versions. * Current profile membership/lifecycle authorizes unchanged pins. Historical * versions additionally require the authenticated exact-version bundle read. */ export declare function reconcileSkillSessionIfSafe(sessionId: string, profileId: string, options?: SessionReconciliationOptions): Promise; /** Metadata only: what an automatic migration retired, never payloads. */ export interface SessionPinMigration { profileId: string; fromRevision: string; toRevision: string; retainedLoadedCount: number; retiredLoadedCount: number; retired: SessionSelectionDelta[]; archivePath: string; receiptPath: string; } export type SessionPinRenewalOutcome = { outcome: "unchanged"; } | { outcome: "renewed"; } | { outcome: "migrated"; migration: SessionPinMigration; }; export interface SessionPinRenewalOptions extends SessionReconciliationOptions { /** * Managed hooks only: when renewal is refused because the same profile moved * on, apply the explicit reconciliation plan to the authenticated current * revision instead. Every other failure keeps its existing refusal. */ migrate?: boolean; /** * Required for `migrate`: the prompt's directory (the hook input's cwd). * Without it, or while it or an ancestor holds a project lock, migration * does not run. */ projectDir?: string; /** * Managed hooks only: the time the caller has left for this renewal. The * renewal uses the smaller of this and SESSION_RENEWAL_TIMEOUT_MS, so it * reports its own SESSION_RENEWAL_TIMEOUT before an outer layer kills it. */ budgetMs?: number; /** * Background renewal only: the budget ceiling is * BACKGROUND_SESSION_RENEWAL_BUDGET_MS instead of SESSION_RENEWAL_TIMEOUT_MS. */ background?: boolean; /** * Renew once the pin is at least this old (default: only after it expired). * Background renew-ahead passes SESSION_RENEW_AHEAD_AGE_MS. The renewal * itself and every check it makes are unchanged; only when it may start is. */ renewAfterMs?: number; } /** * A refused renewal whose automatic migration could not finish within the * renewal deadline. It keeps the refusal code; the fixed `migration` marker * lets the hook say why without carrying free text across its boundary. */ export declare class SessionPinMigrationTimeout extends SkillSelectionError { readonly migration: "timeout"; constructor(); } export declare function renewSkillSessionPin(sessionId: string, profileId: string, options?: SessionPinRenewalOptions): Promise; /** Explicit review for changed loaded selections or intentional profile migration. */ export declare function reconcileSkillSession(input: SessionReconciliationInput, options?: SessionReconciliationOptions): Promise<{ applied: false; plan: { schemaVersion: number; issuedAt: string; expiresAt: string; sessionId: string; sessionPath: string; before: { receiptSha256: string; generation: number; profileId: string; profileRevision: string; selectionCount: number; }; target: { authority: string; workspaceId: string; profileId: string; profileRevision: string; profileSha256: string; selectionCount: number; }; replacementGeneration: number; retainedLoadedCount: number; retiredLoadedCount: number; selectionDelta: SessionSelectionDelta[]; addedSelections: SessionSelectionMetadata[]; scope: string; }; planDigest: string; archivePath: undefined; receiptPath: undefined; beforeSha256: string; afterSha256: undefined; } | { archivePath: string; receiptPath: string; beforeSha256: string; afterSha256: string; applied: true; plan: { schemaVersion: number; issuedAt: string; expiresAt: string; sessionId: string; sessionPath: string; before: { receiptSha256: string; generation: number; profileId: string; profileRevision: string; selectionCount: number; }; target: { authority: string; workspaceId: string; profileId: string; profileRevision: string; profileSha256: string; selectionCount: number; }; replacementGeneration: number; retainedLoadedCount: number; retiredLoadedCount: number; selectionDelta: SessionSelectionDelta[]; addedSelections: SessionSelectionMetadata[]; scope: string; }; planDigest: string; }>;