/**
 * SessionActivitySentinel — Monitors running sessions for undigested activity.
 *
 * The sentinel is a background process that runs inside the Instar server,
 * watching for sessions that have accumulated unprocessed activity. It creates
 * mid-session "mini-digests" using an LLM, and produces a synthesis when
 * sessions complete.
 *
 * Trigger points:
 *   1. Periodic scan (every 30-60 min) — checks running sessions
 *   2. Session completion (sessionComplete event) — creates synthesis
 *   3. On-demand (API/CLI) — manual digest trigger
 *
 * Concurrency:
 *   - Idempotent via hash(sessionId + startedAt + endedAt) digest keys
 *   - Dormant sessions skipped (lastActivity <= lastDigest)
 *   - Minimum activity threshold prevents noisy digests
 *
 * LLM failure handling:
 *   - Failed digests saved to pending queue
 *   - Exponential backoff: 1min, 5min, 15min
 *   - After 3 retries, raw content archived
 *
 * Implements Phase 3 of PROP-memory-architecture.md v3.1.
 */
import type { Session } from '../core/types.js';
import type { IntelligenceProvider } from '../core/types.js';
import { EpisodicMemory, type ActivityDigest } from '../memory/EpisodicMemory.js';
import { type TelegramLogEntry } from '../memory/ActivityPartitioner.js';
import type { SemanticMemory } from '../memory/SemanticMemory.js';
export interface SentinelConfig {
    stateDir: string;
    intelligence: IntelligenceProvider;
    /** Function to get current running sessions */
    getActiveSessions: () => Session[];
    /** Function to capture tmux output for a session */
    captureSessionOutput: (tmuxSession: string) => string | null;
    /** Function to get Telegram messages for a topic */
    getTelegramMessages?: (topicId: number, since?: string) => TelegramLogEntry[];
    /** Function to get the Telegram topic linked to a session */
    getTopicForSession?: (tmuxSession: string) => number | null;
    /** Max retries before archiving pending content (default: 3) */
    maxRetries?: number;
    /**
     * Optional SemanticMemory instance. When provided, the digest LLM is asked
     * to also extract typed entities + relationships, and those are materialized
     * into the knowledge graph with provenance back to the source session.
     * When absent, digests are persisted with `entities: []` — graceful
     * degradation.
     */
    semanticMemory?: SemanticMemory;
}
export interface SentinelReport {
    scannedAt: string;
    sessionsScanned: number;
    digestsCreated: number;
    sessionsSkipped: number;
    errors: Array<{
        sessionId: string;
        error: string;
    }>;
}
export interface SynthesisReport {
    sessionId: string;
    digestCount: number;
    synthesisCreated: boolean;
    error?: string;
}
/**
 * Resolve the periodic-scan interval in milliseconds from config.
 *
 * Returns `null` when the periodic scan is disabled (enabled === false).
 * Otherwise returns the interval clamped to a 5-minute floor (a faster
 * cadence wastes LLM budget — scan() skips dormant sessions and enforces a
 * minimum-activity threshold anyway, so there's no value in scanning more
 * often than every few minutes). Default is 30 minutes.
 *
 * Extracted as a pure function so the clamp / default / disabled logic is
 * unit-testable without standing up the full server bootstrap.
 */
export declare function resolveSentinelScanIntervalMs(cfg?: {
    enabled?: boolean;
    scanIntervalMinutes?: number;
}): number | null;
export declare class SessionActivitySentinel {
    private readonly config;
    private readonly episodicMemory;
    private readonly partitioner;
    private readonly maxRetries;
    constructor(config: SentinelConfig);
    /**
     * Scan all running sessions for undigested activity.
     * Called periodically (every 30-60 min) by the scheduler.
     */
    scan(): Promise<SentinelReport>;
    /**
     * Digest a specific session's recent activity.
     * Returns activity digests created.
     */
    digestActivity(session: Session, lastDigestedAt?: string): Promise<ActivityDigest[]>;
    /**
     * Synthesize all mini-digests into a session-level summary.
     * Called when a session completes.
     */
    synthesizeSession(session: Session): Promise<SynthesisReport>;
    /**
     * Get the underlying EpisodicMemory instance (for route wiring).
     */
    getEpisodicMemory(): EpisodicMemory;
    private digestUnit;
    /**
     * Materialize extracted entities into SemanticMemory.
     *
     * Two-pass algorithm:
     *   1. For each entity, dedupe against the existing graph via findByName.
     *      If the entity exists, reuse its ID. Otherwise call remember() to
     *      create it. Build a name→id map covering both reused and newly-created
     *      entities in this batch.
     *   2. For each entity's relationships, resolve the target by name. Check
     *      the batch map first (intra-digest references), then findByName for
     *      cross-digest references. Unresolved targets are dropped silently —
     *      they'll naturally resolve when a future digest mentions both names.
     *
     * Confidence is set to 0.7 — matching the MEMORY.md migration default. This
     * reflects observation-grade certainty (the LLM extracted from real
     * conversation content), not user-asserted certainty (which would be 0.95).
     *
     * Returns the array of entity IDs (existing + newly-created) for
     * traceability via ActivityDigest.entities.
     */
    private materializeEntities;
    private buildDigestPrompt;
    private parseDigestResponse;
    /**
     * Validate and normalize the entities array from the LLM response.
     *
     * Malformed entries are dropped (logged at warn level) rather than failing
     * the whole digest. The digest summary remains useful even when entity
     * extraction degrades. Type and relation values not in the enums are
     * filtered out at this step rather than at materialization, so the
     * SemanticMemory.remember() call never receives an invalid type.
     */
    private parseExtractedEntities;
    private buildSynthesis;
    private buildSynthesisPrompt;
    private parseSynthesisResponse;
    private retryPending;
    private formatUnitForPending;
    private truncate;
}
//# sourceMappingURL=SessionActivitySentinel.d.ts.map