<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Embedding Model
    |--------------------------------------------------------------------------
    |
    | The model used to generate embeddings for agent memory storage
    | and retrieval. text-embedding-3-small is the most cost-effective.
    |
    */
    'embedding_provider' => env('MEMORY_EMBEDDING_PROVIDER', 'openai'),

    'embedding_model' => env('MEMORY_EMBEDDING_MODEL', 'text-embedding-3-small'),

    /*
    |--------------------------------------------------------------------------
    | Embedding Dimensions
    |--------------------------------------------------------------------------
    |
    | Number of dimensions for the embedding vectors. Must match the
    | vector column size in the memories table migration.
    |
    */
    'embedding_dimensions' => 1536,

    /*
    |--------------------------------------------------------------------------
    | Memory Nudge (Hermes-inspired closed learning loop)
    |--------------------------------------------------------------------------
    |
    | When a team opts in (settings['memory_nudge_enabled'] === true), agents
    | whose un-memorialized activity reaches this threshold receive an in-prompt
    | reminder to persist durable learnings via the memory store tool. This
    | complements the post-hoc distill/consolidate crons; it does not write
    | memories itself. Off by default at the team level.
    |
    */
    'nudge' => [
        'execution_threshold' => (int) env('MEMORY_NUDGE_EXECUTION_THRESHOLD', 5),
    ],

    /*
    |--------------------------------------------------------------------------
    | Memory TTL (Days)
    |--------------------------------------------------------------------------
    |
    | How many days to keep memories before pruning. Set to 0 to disable.
    |
    */
    'ttl_days' => (int) env('MEMORY_TTL_DAYS', 90),

    /*
    |--------------------------------------------------------------------------
    | Top-K Retrieval
    |--------------------------------------------------------------------------
    |
    | Number of most relevant memories to retrieve for context injection.
    |
    */
    'top_k' => (int) env('MEMORY_TOP_K', 5),

    /*
    |--------------------------------------------------------------------------
    | Similarity Threshold
    |--------------------------------------------------------------------------
    |
    | Minimum cosine similarity score (0-1) for memories to be included.
    | Higher values mean stricter matching.
    |
    */
    'similarity_threshold' => (float) env('MEMORY_SIMILARITY_THRESHOLD', 0.7),

    /*
    |--------------------------------------------------------------------------
    | Max Chunk Size
    |--------------------------------------------------------------------------
    |
    | Maximum character count per memory chunk when storing execution outputs.
    |
    */
    'max_chunk_size' => 2000,

    /*
    |--------------------------------------------------------------------------
    | Enabled
    |--------------------------------------------------------------------------
    |
    | Master switch for the memory system. When disabled, no memories
    | are stored or retrieved during agent execution.
    |
    */
    'enabled' => (bool) env('MEMORY_ENABLED', true),

    'auto_flush_enabled' => (bool) env('MEMORY_AUTO_FLUSH_ENABLED', true),

    /*
    |--------------------------------------------------------------------------
    | Composite Scoring Weights
    |--------------------------------------------------------------------------
    |
    | Weights for the composite memory scoring formula:
    | score = semantic_weight * similarity + recency_weight * decay + importance_weight * importance
    | Weights should sum to 1.0 for normalized scoring.
    |
    */
    'scoring' => [
        'semantic_weight' => (float) env('MEMORY_SEMANTIC_WEIGHT', 0.5),
        'recency_weight' => (float) env('MEMORY_RECENCY_WEIGHT', 0.3),
        'importance_weight' => (float) env('MEMORY_IMPORTANCE_WEIGHT', 0.2),
        'half_life_days' => (int) env('MEMORY_HALF_LIFE_DAYS', 7),
    ],

    /*
    |--------------------------------------------------------------------------
    | Write Gate (Semantic Deduplication)
    |--------------------------------------------------------------------------
    |
    | Before storing a new memory, check for near-duplicates using hash and
    | vector similarity. skip_threshold: exact semantic match (discard).
    | update_threshold: similar enough to merge (LLM-assisted).
    |
    */
    'write_gate' => [
        'enabled' => (bool) env('MEMORY_WRITE_GATE_ENABLED', true),
        'skip_threshold' => (float) env('MEMORY_SKIP_THRESHOLD', 0.95),
        'update_threshold' => (float) env('MEMORY_UPDATE_THRESHOLD', 0.85),
        'hash_dedup' => true,
    ],

    /*
    |--------------------------------------------------------------------------
    | Memory Consolidation
    |--------------------------------------------------------------------------
    |
    | Periodic merging of similar memories into consolidated summaries.
    | Reduces noise while preserving signal. Runs as a daily batch job.
    |
    */
    'consolidation' => [
        'enabled' => (bool) env('MEMORY_CONSOLIDATION_ENABLED', true),
        'min_memories_per_agent' => 50,
        'min_cluster_size' => 3,
        'similarity_threshold' => 0.85,
        'exclude_newer_than_days' => 7,
        'model' => 'claude-haiku-4-5',
    ],

    /*
    |--------------------------------------------------------------------------
    | Event Distillation
    |--------------------------------------------------------------------------
    |
    | Nightly job (`memory:distill-events`) that reads each team's recent
    | audit-entry stream and distils it into a few durable, high-signal
    | facts stored as `events_digest` memories — borrowed from CraftBot's
    | EVENT_UNPROCESSED.md -> MEMORY.md pass. Runs before consolidation so
    | fresh digests get merged the same night.
    |
    */
    'distillation' => [
        'enabled' => (bool) env('MEMORY_DISTILLATION_ENABLED', true),
        'window_hours' => 24,
        'max_events' => 200,
        'provider' => 'anthropic',
        'model' => 'claude-haiku-4-5',
    ],

    /*
    |--------------------------------------------------------------------------
    | Importance-Weighted Pruning
    |--------------------------------------------------------------------------
    |
    | Pruning considers both age and importance instead of pure TTL.
    | High-importance or frequently-retrieved memories are protected.
    |
    */
    'pruning' => [
        'score_threshold' => (float) env('MEMORY_PRUNE_SCORE_THRESHOLD', 0.05),
        'max_ttl_days' => (int) env('MEMORY_MAX_TTL_DAYS', 365),
        'protect_importance_above' => 0.8,
        'protect_retrieval_above' => 10,
        // Cap on total memories per agent — lowest-scoring are evicted when exceeded
        'max_per_agent' => (int) env('MEMORY_MAX_PER_AGENT', 2000),
    ],

    /*
    |--------------------------------------------------------------------------
    | Unified Search (RRF Fusion)
    |--------------------------------------------------------------------------
    |
    | Reciprocal Rank Fusion across vector memory, knowledge graph, and keyword
    | search. Higher weights = more influence on final ranking.
    |
    */
    'unified_search' => [
        'enabled' => (bool) env('MEMORY_UNIFIED_SEARCH', true),
        'kg_weight' => 2.0,
        'vector_weight' => 1.0,
        'keyword_weight' => 0.5,
        'rrf_k' => 60,
    ],

    /*
    |--------------------------------------------------------------------------
    | Contextual RAG
    |--------------------------------------------------------------------------
    |
    | When enabled, each stored chunk gets an LLM-generated 64-token context
    | prepended before re-embedding (Anthropic Contextual Retrieval technique).
    | Significantly improves retrieval for ambiguous or out-of-context chunks.
    | Cost: ~1 Haiku call per chunk at index time.
    |
    */
    'contextual_rag' => [
        'enabled' => (bool) env('MEMORY_CONTEXTUAL_RAG_ENABLED', false),
        'model' => env('MEMORY_CONTEXTUAL_RAG_MODEL', 'claude-haiku-4-5'),
    ],

    /*
    |--------------------------------------------------------------------------
    | Visibility & Cross-Agent Sharing
    |--------------------------------------------------------------------------
    |
    | Controls when private memories are auto-promoted to project scope.
    | A memory needs both minimum retrievals AND importance to be shared.
    |
    */
    'visibility' => [
        'auto_promote_retrievals' => 3,
        'auto_promote_min_importance' => 0.7,
    ],

    /*
    |--------------------------------------------------------------------------
    | Proposal Workflow (Knowledge Nominations)
    |--------------------------------------------------------------------------
    |
    | When extractors_enabled is true, ExtractSuccessPattern/FailureLesson
    | route through the Proposed tier instead of writing directly to the
    | curated tier. A heuristic auditor (AuditMemoryProposalsAction) then
    | auto-approves obvious wins, auto-rejects obvious noise, and queues the
    | rest for human review.
    |
    | Existing installs keep extractors_enabled=false → no behavior change.
    |
    */
    'proposal_workflow' => [
        'extractors_enabled' => (bool) env('MEMORY_PROPOSAL_WORKFLOW', false),
        'auto_approve_threshold' => (float) env('MEMORY_PROPOSAL_AUTO_APPROVE', 0.85),
        'min_content_length' => (int) env('MEMORY_PROPOSAL_MIN_LENGTH', 30),
        'min_confidence' => (float) env('MEMORY_PROPOSAL_MIN_CONFIDENCE', 0.3),
    ],

    /*
    |--------------------------------------------------------------------------
    | Cross-Corpus Contradiction Scan (RoBrain Synthesis)
    |--------------------------------------------------------------------------
    |
    | A scheduled batch job (`memory:detect-contradictions`) that scans the
    | whole memory corpus for pairs of beliefs that reverse each other —
    | contradictions that only emerge later, across sessions, which the
    | per-write dedup gate cannot see. Flagged pairs are surfaced for human
    | review in the Memory Browser.
    |
    | The scheduled run is OFF by default (it costs one Haiku call per team
    | per run). The manual MCP scan ignores `enabled` — it is opt-in already.
    | Candidate pairs are memories whose cosine similarity falls in the band
    | [min_similarity, max_similarity]: similar enough to be about the same
    | thing, not so similar they are duplicates the write gate already merged.
    |
    */
    'contradiction_scan' => [
        'enabled' => (bool) env('MEMORY_CONTRADICTION_SCAN', false),
        'model' => env('MEMORY_CONTRADICTION_MODEL', 'claude-haiku-4-5'),
        'min_similarity' => (float) env('MEMORY_CONTRADICTION_MIN_SIMILARITY', 0.55),
        'max_similarity' => (float) env('MEMORY_CONTRADICTION_MAX_SIMILARITY', 0.92),
        'candidate_limit' => (int) env('MEMORY_CONTRADICTION_CANDIDATE_LIMIT', 60),
        'max_pairs' => (int) env('MEMORY_CONTRADICTION_MAX_PAIRS', 25),
    ],

];
