import { QmdSearchResult } from './types.js';
import { QmdClient } from './qmd.js';
import './port-hqGnoStS.js';

/**
 * Parallel Specialized Retrieval (v9.1 — ASMR-inspired)
 *
 * Replaces single-pass hybrid search with three parallel specialized agents:
 *
 *   1. DirectFactAgent  — entity-first lookup via entity filename index (<5ms)
 *   2. ContextualAgent  — existing hybridSearch (same cost as current single-pass)
 *   3. TemporalAgent    — temporal index prefilter + recency scoring (<10ms)
 *
 * All agents run via Promise.all(), so total latency = max(agents), not sum.
 * No new LLM inference is introduced — agents reuse existing search primitives.
 *
 * Graceful degradation: any agent error/timeout does not block the others.
 *
 * References:
 *   - Supermemory ASMR: https://blog.supermemory.ai/...
 *   - Spec: docs/ideas/parallel-specialized-retrieval.md
 */

type SearchAgentSource = "direct" | "contextual" | "temporal";
interface ParallelSearchResult extends QmdSearchResult {
    /** Which retrieval agent produced this result. */
    agentSource: SearchAgentSource;
}
/** Source-based weights applied during merge. Higher = higher confidence/precision. */
declare const PARALLEL_AGENT_WEIGHTS: Record<SearchAgentSource, number>;
/**
 * Decide whether a given agent should run for this query.
 * Agent 1 (direct) is the only one that may be skipped — when the query
 * contains no proper-noun-like tokens, entity lookups will return nothing.
 * Agents 2 (contextual) and 3 (temporal) always run.
 */
declare function shouldRunAgent(agent: SearchAgentSource, query: string, knownEntityCount: number): boolean;
/**
 * Direct Facts Agent — entity-first retrieval.
 *
 * Reads entity filenames from the entities directory and scores them by
 * keyword overlap with the query. Returns matching entity file paths as
 * QmdSearchResult[]. Zero LLM inference; pure file I/O.
 *
 * Cost: readdir on entities/ + filename scoring. Typically <5ms.
 *
 */
declare function runDirectAgent(query: string, memoryDir: string, maxResults?: number): Promise<ParallelSearchResult[]>;
/**
 * Temporal Agent — recency-focused retrieval.
 *
 * Uses the temporal index (index_time.json) to find memories from the
 * recent time window derived from the query (default: last 7 days).
 * Combines recency decay with query-filename token overlap so results stay
 * relevant to the query content, not just recency. Zero LLM inference; File I/O + math.
 *
 * Cost: read index_time.json + date range scan. Typically <10ms.
 */
declare function runTemporalAgent(query: string, memoryDir: string, maxResults?: number, 
/** Optional candidate set from query-aware prefilter. Applied BEFORE the top-K cap so
 * in-scope entries are not displaced by newer out-of-scope entries. */
candidatePaths?: Set<string> | null): Promise<ParallelSearchResult[]>;
/**
 * Contextual Agent — broad semantic/hybrid search.
 *
 * Delegates to the existing hybridSearch (BM25 + vector). Same cost as the
 * current single-pass retrieval — this agent introduces no additional LLM calls.
 */
declare function runContextualAgent(query: string, qmd: QmdClient, collection: string | undefined, maxResults: number, signal?: AbortSignal): Promise<ParallelSearchResult[]>;
/**
 * Merge pre-computed direct and temporal agent results with contextual results.
 *
 * This is the merge half of augmentWithDirectAndTemporal. Expose it separately so
 * callers that started agents concurrently with the contextual search (for true
 * parallel latency) can merge after all three complete, without re-running agents.
 *
 * Handles the same cases as augmentWithDirectAndTemporal:
 * - Returns contextual unchanged (no reweighting) when both agents return nothing
 * - Applies maxResults cap in all paths
 * - Populates snippets for results discovered only by specialized agents
 */
declare function mergeWithAgentResults(contextualResults: QmdSearchResult[], directResults: ParallelSearchResult[], temporalResults: ParallelSearchResult[], weights: Record<SearchAgentSource, number>, maxResults: number): Promise<QmdSearchResult[]>;
/**
 * Augment a set of contextual (hybridSearch) results with direct and temporal
 * agent results, returning a merged, deduplicated list.
 *
 * **Test helper** — production recall uses the inline orchestrator path which
 * passes namespace-specific directories to each agent. This function uses a
 * simplified single-directory contract suitable for unit tests.
 *
 * Direct and temporal agents run in parallel, then all three are merged with
 * configurable weights.
 *
 * Contextual weight is always applied to `contextualResults` so scoring is
 * consistent regardless of whether direct/temporal agents return anything.
 *
 * Snippets: snippets from contextual results are preserved during merge. For
 * results discovered only by direct/temporal agents (no contextual match),
 * the first ~200 chars of the file are read as a fallback preview so downstream
 * formatters and rerankers have content to work with, not just a path.
 */
declare function augmentWithDirectAndTemporal(query: string, memoryDir: string, contextualResults: QmdSearchResult[], weights: Record<SearchAgentSource, number>, maxPerAgent: number, maxResults: number, 
/** Optional candidate set from query-aware prefilter (time/tag scoped). Agents' results are
 * filtered to this set when provided so recall stays within the operator-specified scope. */
candidatePaths?: Set<string> | null): Promise<QmdSearchResult[]>;
interface ParallelRetrievalOptions {
    collection?: string;
    maxResults?: number;
    signal?: AbortSignal;
    /** If true, skip the contextual agent (hybridSearch is already running externally). */
    skipContextual?: boolean;
    /** Max results per agent (capped before merge). */
    maxResultsPerAgent?: number;
    /** Override default agent weights. Falls back to PARALLEL_AGENT_WEIGHTS when absent. */
    agentWeights?: Record<SearchAgentSource, number>;
}
/**
 * Run parallel specialized retrieval agents and merge results.
 *
 * When `skipContextual` is true, only the direct and temporal agents run.
 * This is the intended usage inside the orchestrator, where hybridSearch
 * already runs as the primary search — we augment with the cheap extra agents.
 *
 * Total latency = max(agent_latencies), not sum.
 * Agent errors are isolated — others still return results.
 */
declare function parallelRetrieval(query: string, qmd: QmdClient, memoryDir: string, options?: ParallelRetrievalOptions): Promise<QmdSearchResult[]>;

export { PARALLEL_AGENT_WEIGHTS, type ParallelRetrievalOptions, type ParallelSearchResult, type SearchAgentSource, augmentWithDirectAndTemporal, mergeWithAgentResults, parallelRetrieval, runContextualAgent, runDirectAgent, runTemporalAgent, shouldRunAgent };
