Member Junction
    Preparing search index...

    Parameters for executing a search.

    interface SearchParams {
        AIAgentID?: string;
        AISkillID?: string;
        Audience?: SearchAudience;
        Filters?: SearchFilters;
        FusionWeightsOverride?: FusionWeightsByProvider;
        MaxResults?: number;
        MinScore?: number;
        Mode?: SearchMode;
        PermissionOverfetchFactor?: number;
        Query: string;
        ScopeIDs?: string[];
        SearchContext?: SearchContext;
    }
    Index

    Properties

    AIAgentID?: string

    Optional ID of the AIAgent on whose behalf this search runs. When set, the engine stamps it onto the SearchExecutionLog row so analytics can attribute usage back to the calling agent. Pass-through only — permission resolution does its own agent lookup separately via the resolver caller.

    AISkillID?: string

    Optional ID of the AI Skill active for this search. Stamped onto the SearchExecutionLog row alongside AIAgentID.

    A skill is a search principal in its own right — activating one can reach a scope the user's own roles do not grant — so a log that records only the agent cannot reconstruct why a scope was reachable. Pass-through only: the engine does not resolve entitlement itself, the caller that selects scopes does.

    Audience?: SearchAudience

    Everyone besides contextUser who will see this search's results. When set, a result survives only if every reader may read it: the engine runs its permission safety net (entity read, row filters, ownership, and the origin-record gate for derived content) once for the caller and once per reader, and keeps the intersection. Use it for a shared conversation, where the asker's reach is the ceiling but not the floor — a document one participant can't open must not be quoted to the room because another participant could. Readers never widen a result set. The engine does not resolve IDs to users, and nothing in the GraphQL surface sets this today.

    Four rules for callers:

    1. Pass hydrated UserInfo objects (e.g. from UserCache): every reader needs a non-empty ID and a UserRoles array. A malformed audience — Readers not an array, a null reader, a reader with no ID or with no UserRoles array — fails the search (Success: false, an error starting "SearchEngine: invalid Audience"); it is never skipped. UserRoles: [] is legitimate and reads nothing, which empties the result.
    2. Expect no storage hits. storage-file results are dropped under an audience: storage hits are re-checked (account permission and the tracked-file rule) for the caller only; per-reader storage checks are a follow-up.
    3. Show the room fused/final results. streamSearch's provider events never carry results (only providerName, durationMs and resultCount, for progress): they arrive before any permission pass.
    4. Check each reader's scope entitlement yourself. Scope entitlement (SearchScopePermission), ServerDerived dimensions, scope ExtraFilter/MetadataFilter templates and vector push-down are evaluated for the caller only. A host must check that every reader may use the scopes it passes, and must not rely on dimension-only bounds to keep a room inside its reach.

    Two further limits:

    • SourceCounts, and a streamed provider event's resultCount, are counted before the permission and audience passes, so they reveal the caller's unfiltered reach to anyone shown them. Don't show them to a room.
    • The result cache keys on reader IDs. Within its 30 s TTL, a reader object with the same ID but different hydration (roles changed, say) gets the cached verdict.
    Filters?: SearchFilters

    Optional filters to narrow results

    FusionWeightsOverride?: FusionWeightsByProvider

    Optional per-agent fusion weight override. When set, the engine applies these weights during cross-scope RRF fusion and honors them per-provider when weighting within-scope lists. Resolution order: this > SearchScope.ScopeConfig.fusionWeights > engine defaults. Example: { vector: 2.0, fulltext: 1.0, entity: 1.0, storage: 1.0 }.

    MaxResults?: number

    Maximum number of results to return (default: 20)

    MinScore?: number

    Minimum semantic similarity (0-1), applied to the vector lane's own score BEFORE fusion. Vector hits below it are dropped. Keyword, full-text, tag and storage hits have no numeric floor (a hit already means the text matched), and the fused Score is never compared to it.

    Mode?: SearchMode

    Search mode: 'full' applies enrichment, 'preview' skips it for speed

    PermissionOverfetchFactor?: number

    Optional per-provider topK overfetch multiplier. Compensates for residual late permission filtering by requesting more candidates from each provider than the caller strictly wants. The final result count is still bounded by MaxResults. When not set, the largest permissionOverfetchFactor declared by the resolved scopes' ScopeConfig applies, else the engine default (2). Whatever the source, the factor is clamped to 1–20.

    Query: string

    The search query text

    ScopeIDs?: string[]

    Optional array of MJ: Search Scopes record IDs. When provided, the engine resolves each scope's metadata (providers, external indexes, entities, storage accounts) and runs only those constrained providers. Results from multiple scopes are combined via cross-scope RRF fusion. When omitted/empty, behaves as if the Global scope was used (backward compatible). A single scope marked IsGlobal=true is also treated as no filter.

    Every ID must name a scope that is active right now. If any is inactive, expired, or not found, the search is refused (Success: false, an error naming the scope) — it is never run without that scope, and never widened to a global search.

    SearchContext?: SearchContext

    Optional multi-tenant runtime context. Interpolated into each scope's Nunjucks-rendered MetadataFilter, ExtraFilter, UserSearchString, and FolderPath values so the same scope definition can serve many tenants. See Section 9 of plans/search-scopes-rag-plus.md.