/**
 * Multi-Graph Memory (MAGMA/SYNAPSE-inspired, v8.2)
 *
 * Maintains three typed edge graphs:
 *   entity.jsonl  — memories sharing a named entity (entityRef)
 *   time.jsonl    — consecutive memories in the same thread/session
 *   causal.jsonl  — memories linked by causal language heuristics
 *
 * Stored under `<memoryDir>/state/graphs/`.
 * All writes are fail-open: errors are caught/logged, never thrown.
 */
type GraphType = "entity" | "time" | "causal";
interface GraphEdge {
    from: string;
    to: string;
    type: GraphType;
    weight: number;
    label: string;
    ts: string;
    confidence?: number;
    lastReinforcedAt?: string;
}
interface GraphConfig {
    multiGraphMemoryEnabled: boolean;
    entityGraphEnabled: boolean;
    timeGraphEnabled: boolean;
    causalGraphEnabled: boolean;
    maxGraphTraversalSteps: number;
    graphActivationDecay: number;
    maxEntityGraphEdgesPerMemory: number;
    graphLateralInhibitionEnabled: boolean;
    graphLateralInhibitionBeta: number;
    graphLateralInhibitionTopM: number;
    /**
     * Issue #681 PR 3/3 — minimum edge confidence required for traversal.
     * Edges with confidence below this floor are pruned. Legacy edges
     * (no `confidence` field) are treated as 1.0 and always pass.
     * Range `[0, 1]`. Default 0.2.
     */
    graphTraversalConfidenceFloor: number;
    /**
     * Issue #681 PR 3/3 — number of PageRank-style refinement iterations
     * applied on top of BFS activation. Set to 0 to disable refinement
     * and return raw BFS scores. Default 8.
     */
    graphTraversalPageRankIterations: number;
}
/** Default minimum edge confidence required for traversal (issue #681 PR 3/3). */
declare const DEFAULT_GRAPH_TRAVERSAL_CONFIDENCE_FLOOR = 0.2;
/** Default PageRank-style refinement iteration count (issue #681 PR 3/3). */
declare const DEFAULT_GRAPH_TRAVERSAL_PAGERANK_ITERATIONS = 8;
declare const CAUSAL_PHRASES: string[];
declare function graphsDir(memoryDir: string): string;
declare function graphFilePath(memoryDir: string, type: GraphType): string;
declare function ensureGraphsDir(memoryDir: string): Promise<void>;
/**
 * Run `fn` while holding the write lock for the given graph JSONL file.
 *
 * The lock is keyed by absolute file path so concurrent writes to
 * different graph types proceed independently. The chain recovers from
 * rejection (gotcha #40) so a single I/O failure does not poison all
 * future writers, but the original error is still surfaced to the
 * caller of `withGraphWriteLock`.
 */
declare function withGraphWriteLock<T>(filePath: string, fn: () => Promise<T>): Promise<T>;
declare function appendEdge(memoryDir: string, edge: GraphEdge): Promise<void>;
/**
 * Read all edges of a given type from the JSONL file.
 * Returns [] if the file doesn't exist or any read error occurs (fail-open).
 *
 * Production traversal callers (recall/PageRank) depend on this fail-open
 * posture so a temporarily missing or unreadable graph file never blocks
 * a recall. Maintenance jobs that need to distinguish ENOENT from real
 * I/O failures must use {@link readEdgesStrict} instead.
 */
declare function readEdges(memoryDir: string, type: GraphType): Promise<GraphEdge[]>;
/**
 * Same as {@link readEdges} but only swallows `ENOENT`; all other read
 * errors (`EACCES`, `EIO`, …) are propagated. Used by the graph-edge
 * decay maintenance job so I/O outages surface as a failed run instead
 * of being silently reported as "no edges to decay" (issue #729 /
 * Codex P1, line 120).
 */
declare function readEdgesStrict(memoryDir: string, type: GraphType): Promise<GraphEdge[]>;
/**
 * Read edges from all enabled graph types.
 */
declare function readAllEdges(memoryDir: string, config: Pick<GraphConfig, "entityGraphEnabled" | "timeGraphEnabled" | "causalGraphEnabled">): Promise<GraphEdge[]>;
interface GraphHealthFileStats {
    type: GraphType;
    filePath: string;
    exists: boolean;
    totalLines: number;
    validEdges: number;
    corruptLines: number;
    uniqueNodes: number;
}
interface GraphHealthReport {
    generatedAt: string;
    enabledTypes: GraphType[];
    totals: {
        totalLines: number;
        validEdges: number;
        corruptLines: number;
        uniqueNodes: number;
    };
    files: GraphHealthFileStats[];
    repairGuidance?: string[];
}
declare function analyzeGraphHealth(memoryDir: string, options?: {
    entityGraphEnabled?: boolean;
    timeGraphEnabled?: boolean;
    causalGraphEnabled?: boolean;
    includeRepairGuidance?: boolean;
}): Promise<GraphHealthReport>;
/**
 * Detect causal signal phrases in text. Returns the first matched phrase, or null.
 */
declare function detectCausalPhrase(text: string): string | null;
/**
 * GraphIndex — builds and updates the three memory graphs.
 *
 * Usage (orchestrator):
 *   this.graphIndex = new GraphIndex(config.memoryDir, config);
 *
 *   // After each memory write:
 *   await this.graphIndex.onMemoryWritten(memoryPath, frontmatter, threadId, recentInThread);
 */
declare class GraphIndex {
    private readonly memoryDir;
    private readonly cfg;
    private edgeCache;
    private static readonly EDGE_CACHE_TTL_MS;
    constructor(memoryDir: string, cfg: GraphConfig);
    /** Clear the edge cache so the next spreadingActivation() re-reads from disk.
     *  Call after any code path that appends edges outside of onMemoryWritten(). */
    invalidateEdgeCache(): void;
    private loadEdgesCached;
    /**
     * Called after a memory is written to disk.
     *
     * @param memoryPath - relative path from memoryDir (e.g. "facts/2026-02-22/abc.md")
     * @param entityRef  - entityRef frontmatter field (if any)
     * @param content    - full memory text (for causal detection)
     * @param created    - ISO timestamp of this memory
     * @param threadId   - current thread ID (for time graph)
     * @param recentInThread - paths of the N most-recent memories in this thread (for time graph)
     * @param entitySiblings - paths of other memories that share the same entityRef (for entity graph)
     */
    onMemoryWritten(opts: {
        memoryPath: string;
        entityRef?: string;
        content: string;
        created: string;
        threadId?: string;
        recentInThread?: string[];
        entitySiblings?: string[];
        causalPredecessor?: string;
    }): Promise<void>;
    /**
     * Spreading activation BFS (SYNAPSE-inspired).
     *
     * Starting from `seeds`, traverse the combined graph for up to `maxSteps` hops.
     * Each candidate gets an activation score = edge.weight × edgeConfidence × decay^hop.
     *
     * Issue #681 PR 3/3 — confidence-aware traversal:
     *   - Each edge's `weight` is multiplied by its `confidence` (legacy edges
     *     missing `confidence` are treated as 1.0, preserving prior behavior).
     *   - Edges with `confidence < graphTraversalConfidenceFloor` are pruned and
     *     contribute neither activation nor downstream neighbors.
     *   - When `graphTraversalPageRankIterations > 0`, an additional PageRank-
     *     style refinement pass redistributes activation along confidence-weighted
     *     edges, sharpening the ranking among multi-hop candidates.
     *   - Per-result provenance includes the highest-confidence edge that landed
     *     on each candidate, so the X-ray surface can attribute pruning and
     *     ranking decisions back to specific edges.
     *
     * @param seeds    - initial memory paths to expand from (e.g. QMD top results)
     * @param maxSteps - max BFS hops (from config: maxGraphTraversalSteps)
     * @returns Array of {path, score, edgeConfidence, ...} sorted descending, not including seed paths
     */
    spreadingActivation(seeds: string[], maxSteps?: number, opts?: {
        /**
         * Issue #681 — when `true`, bypasses the configured
         * `graphTraversalConfidenceFloor` and includes low-confidence
         * edges in traversal.  Equivalent to forcing the floor to `0`.
         * Default `false` (floor from config is applied).
         */
        includeLowConfidence?: boolean;
    }): Promise<Array<{
        path: string;
        score: number;
        seed: string;
        hopDepth: number;
        decayedWeight: number;
        graphType: "entity" | "time" | "causal";
        /**
         * Confidence of the edge that produced this candidate's recorded
         * provenance (the strongest edge along the chosen entry path).
         * In `[0, 1]`. Legacy edges without `confidence` surface as 1.0.
         */
        edgeConfidence: number;
    }>>;
}
/**
 * Clamp `graphTraversalConfidenceFloor` into the legal range `[0, 1]`.
 * Non-finite or non-numeric values fall back to the documented default
 * so misconfiguration cannot silently disable the floor or reject every edge.
 *
 * Exported for tests; call sites in `spreadingActivation` use it to make
 * the contract explicit at every boundary.
 */
declare function clampConfidenceFloor(raw: unknown): number;
/**
 * Clamp `graphTraversalPageRankIterations` into a non-negative integer.
 * Negative or non-finite values fall back to 0 (disable refinement) so
 * misconfiguration cannot stall recall in an unbounded loop.
 */
declare function clampPageRankIterations(raw: unknown): number;
/**
 * PageRank-style refinement on top of the BFS activation map.
 *
 * Each iteration redistributes a fraction of every node's score along
 * its outgoing edges, scaled by edge confidence. Confidence below
 * `floor` is filtered out before redistribution, mirroring the BFS
 * pruning rule. Mutates `scores` in place.
 *
 * Exported for tests; in production, call sites pass the same adjacency
 * map already used by BFS so behavior stays consistent.
 */
declare function applyPageRankRefinement(scores: Map<string, number>, adj: Map<string, GraphEdge[]>, opts: {
    iterations: number;
    floor: number;
    damping: number;
}): void;
/**
 * Lateral inhibition (Synapse-inspired).
 *
 * For each node, the top-M higher-activation competitors exert inhibition
 * proportional to their activation difference. Output is clamped to [0, ∞).
 *
 * No sigmoid is applied here — downstream `normalizeGraphActivationScore`
 * already applies x/(1+x) soft squash, so adding a sigmoid would double-
 * normalize and cap graph influence at ~50%.
 *
 * Formula: u_hat_i = max(0, u_i - beta * sum_{k in top-M where u_k > u_i}(u_k - u_i))
 *
 * When beta=0 or topM=0, returns original scores unchanged (no-op).
 */
declare function applyLateralInhibition(scores: Map<string, number>, opts: {
    beta: number;
    topM: number;
}): Map<string, number>;

export { CAUSAL_PHRASES, DEFAULT_GRAPH_TRAVERSAL_CONFIDENCE_FLOOR, DEFAULT_GRAPH_TRAVERSAL_PAGERANK_ITERATIONS, type GraphConfig, type GraphEdge, type GraphHealthFileStats, type GraphHealthReport, GraphIndex, type GraphType, analyzeGraphHealth, appendEdge, applyLateralInhibition, applyPageRankRefinement, clampConfidenceFloor, clampPageRankIterations, detectCausalPhrase, ensureGraphsDir, graphFilePath, graphsDir, readAllEdges, readEdges, readEdgesStrict, withGraphWriteLock };
