/**
 * Layered Discovery Tools — ADR-149 Phase 5d
 *
 * When MCP_LAYERED_DISCOVERY=1, the catalog server handshake exposes only:
 *   pica_sign_in, pica_sign_out, pica_discover, pica_tool_details, pica_execute
 *
 * Agents use pica_discover → pica_tool_details → pica_execute to progressively
 * discover and invoke the full tool set without overwhelming the context window.
 */
import type { ToolDefinition, ToolExecutor, ToolExecutorContext, ToolMetadata, ToolResult } from "./index.js";
/**
 * The fresh-org on-ramp, carried by BOTH pica_discover modes (ADR-294 L3).
 * Static and org-blind by design so the response stays cacheable: the
 * instruction is universal, not org data. One constant, because the ChatGPT
 * app moved to query mode on 2026-08-10 and for sixteen days this line lived
 * only on the full map — the client carrying 92% of tool calls never saw it
 * (FIX_LOG 2026-08-26). Pinned in discovery.test.ts for a hit, a miss and
 * the full map.
 */
export declare const FRESH_ORG_HINT = "on a fresh or empty org, run pica_dashboard_briefing first (through pica_execute where this surface routes calls that way). if the catalog is empty it names the fastest import: paste a spotify album or playlist link.";
/**
 * Words that describe the asker, not the entity. `pica_my_*` tools carry
 * "my" as their DOMAIN segment, so before this filter "start my music
 * catalogue and import my songs" led with pica_my_recent_questions,
 * pica_my_reported_issues and pica_export_my_data — three tools about the
 * caller, for a user who asked to import (prod agent_events, 2026-08-11).
 * Determiners, pronouns, prepositions, conjunctions, auxiliaries and the
 * LIGHT verbs (get / list / find / show / check / run / set …) that name no
 * entity — "get the isrc" is about the ISRC. Eight tools carry a light verb
 * as their first segment (pica_get_agent_activity, pica_find_duplicates,
 * pica_run_work_cascade …); for them the entity is the next segment, and
 * the verb still reaches them through the name text (+1). Domain verbs that
 * discriminate inside a domain (delete, create, update, query, import …)
 * stay. Before the light verbs were listed, "get the isrc from this
 * spotify link" led with pica_get_agent_activity once the noise words that
 * had been propping the import tool up were removed.
 */
export declare const STOP_WORDS: ReadonlySet<string>;
interface CategoryDefinition {
    description: string;
    tools: string[];
    depends_on: string[];
}
/**
 * Hand-maintained category taxonomy for the catalog server.
 * Grouped by natural entity/capability prefix.
 * Cross-server depends_on declarations use "<server-slug>:<category>" format.
 *
 * Exported so `scripts/lint-mcp-tools.ts` can enforce taxonomy completeness
 * (every registered tool ∈ taxonomy ∪ archetype-allowlist) at
 * prepublishOnly time. Sealed after the bug class bit four times — silently
 * for `pica_upload` + `pica_download` since ADR-152, for `pica_submit_feedback`
 * since ADR-184, and again for `pica_report_issue` + `pica_my_reported_issues`
 * (ADR-196 Phase 3) which were the exact failure mode they were designed to
 * catch and so couldn't catch themselves. ~95 further pre-existing violators
 * are grandfathered in `scripts/mcp-tool-lint-baseline.json` for retrofit.
 */
export declare const CATEGORIES: Record<string, CategoryDefinition>;
/**
 * Naive English singular of a search token.
 *
 * A wrong stem is NOT free. An `-es` strip turns `notes` into `not` — and
 * `pica_notes_*` are in CATEGORIES with `notes` as their domain segment, so
 * every query containing the ordinary English word "not" ("works not
 * registered") scored a +6 domain hit on the notes family. The same rule
 * stems `releases`→`releas`, which the query word `release` can never match,
 * defeating the feature in the other direction. Both shipped on 2026-08-24
 * and are pinned by the singularise unit tests.
 *
 * The rules, in order:
 *   - `ies` → `y`   (categories → category)
 *   - `s`   → ""    unless the token ends `ss`/`us`/`is`, none of which is an
 *                   English plural (address, status, analysis stay put)
 *
 * There is deliberately NO `-es` rule. Every domain segment in CATEGORIES is a
 * plain `-s` plural (works, notes, releases, documents, statements, credits,
 * shows…), so `-es` would only ever misfire: it cannot tell `releas+es` from
 * `release+s`, and the wrong branch (`releas`) is one a query word can never
 * reach. `boxes` therefore stems to `boxe` rather than `box` — harmless,
 * because both sides of every comparison run through this same function.
 *
 * Exported for direct unit assertion in
 * `src/__tests__/tools/discovery-ranking.test.ts` — the stem is load-bearing
 * enough that it needs testing without a whole registry behind it.
 */
export declare function singularise(token: string): string;
export declare class DiscoveryTools {
    private executeToolFn;
    private getToolDef;
    /**
     * Registered tool names, for TOOL_NOT_FOUND near-match suggestions.
     * Optional so existing constructions keep compiling; without it the
     * unknown-tool envelope simply carries no suggestions.
     */
    private listToolNames?;
    /**
     * Whether the CURRENT caller can run a tool on this connection. On HTTP
     * the route installs its `allowedToolNames` judgement (ADR-248:
     * audiences ∋ http AND scope held) via `ToolRegistry.setToolVisibility`;
     * the registry's default is audience-only. Absent = every registered
     * tool is treated as callable (legacy constructions, unit harnesses).
     * Consulted by the shortlist (rank visible first, flag the rest) and by
     * tool_details (flag) — never by pica_execute, which the dispatcher
     * already gates.
     */
    private isToolVisible?;
    constructor(executeToolFn: (name: string, args: Record<string, unknown>, ctx?: ToolExecutorContext) => Promise<ToolResult>, getToolDef: (name: string) => {
        definition: ToolDefinition;
        executor: ToolExecutor;
        metadata?: ToolMetadata;
    } | undefined, 
    /**
     * Registered tool names, for TOOL_NOT_FOUND near-match suggestions.
     * Optional so existing constructions keep compiling; without it the
     * unknown-tool envelope simply carries no suggestions.
     */
    listToolNames?: (() => string[]) | undefined, 
    /**
     * Whether the CURRENT caller can run a tool on this connection. On HTTP
     * the route installs its `allowedToolNames` judgement (ADR-248:
     * audiences ∋ http AND scope held) via `ToolRegistry.setToolVisibility`;
     * the registry's default is audience-only. Absent = every registered
     * tool is treated as callable (legacy constructions, unit harnesses).
     * Consulted by the shortlist (rank visible first, flag the rest) and by
     * tool_details (flag) — never by pica_execute, which the dispatcher
     * already gates.
     */
    isToolVisible?: ((name: string) => boolean) | undefined);
    private isVisible;
    /** Shortlist / detail keys added only when the tool is NOT callable here. */
    private surfaceFlags;
    /**
     * Rank registered tool names against a hallucinated or mistyped one.
     *
     * ChatGPT callers routinely invent tool names
     * (`pica_releases_attach_track_by_identifier`, `pica_credit_add`, …) and
     * retry variations instead of consulting pica_tool_details — 26 opaque
     * dispatcher errors in 14 days. Naming the closest real tools breaks that
     * guess-loop.
     *
     * Token overlap alone was not enough: `pica_works_inspekt` overlaps every
     * `pica_works_*` tool on exactly one token, so the top-3 slice fell back to
     * registration order and could drop the obvious answer. Edit distance
     * breaks that tie — pinned by "ranks the nearest EDIT-distance name first"
     * in `src/__tests__/tools/discovery.test.ts`.
     *
     * Deprecated aliases are excluded. The candidate pool is
     * `[...this.tools.keys()]` (index.ts, where DiscoveryTools is constructed),
     * which includes the hidden alias clones `expandAliases` registers — so a
     * typo one letter off an alias (`pica_releases_lists`) was answered with the
     * alias (`pica_releases_list`, since removed in 2.99.0) ranked above its own
     * canonical. Steering a confused caller onto a name we are trying to retire
     * is the opposite of what this envelope is for. TOOL_ALIASES is empty as of
     * 2.99.0, so the filter currently removes nothing; it stays for the next
     * deprecation.
     *
     * Tools the caller cannot run here are excluded too ({@link canSuggest}).
     * The pool is every REGISTERED name, and on HTTP the route narrows what a
     * caller may run through `setToolVisibility` (audience + scope). Without
     * this filter a caller lacking finance scope was offered hidden
     * statements/royalties tools for a statements typo — the same names the
     * hosted route's suggester (`app/api/mcp/tool-not-found.ts`) never offers,
     * because its pool is the caller's callable set.
     */
    private suggestToolNames;
    /** Registered AND callable by this caller on this surface. */
    private canSuggest;
    /**
     * The suggestions and leading note for a name that is not registered. A
     * retired name (RETIRED_TOOLS in deprecations.ts) gets its replacement
     * first (when callable) and a sentence saying what replaced it; any other
     * name gets the similarity ranking unchanged.
     *
     * What is shared with the hosted route (`app/api/mcp/tool-not-found.ts`)
     * is `retiredToolAnswer`, so both name the same replacement — pinned on
     * this side by `src/__tests__/tools/tool-details-contract.test.ts` and on
     * that side by `app/api/mcp/__tests__/tool-not-found.test.ts`. The
     * similarity lists are NOT shared: each side ranks with its own suggester
     * over its own pool, so for non-retired names the two may differ.
     */
    private notFoundAnswer;
    getTools(): Array<{
        definition: ToolDefinition;
        executor: ToolExecutor;
    }>;
    private buildDiscoverTool;
    /**
     * Rank tools against a plain-language job description. Case-insensitive
     * token scoring (no external deps):
     *
     *   +6  the token matches the tool's DOMAIN segment (the `_`-separated
     *       segment after `pica_` — `recordings` in `pica_recordings_delete`)
     *   +3  the token matches any other `_`-separated segment of the name
     *   +1  the token appears anywhere in the searchable text (name +
     *       description/triggers + category name + category description)
     *   +2  (once) an ADJACENT pair of typed query tokens appears, stemmed,
     *       in that text — a phrase names one concept ("pica score"), and
     *       single-token crumbs must not out-rank the tool that says the
     *       phrase; capped below the domain weight so prose never dethrones
     *       the tool that IS the domain
     *
     * Both sides are compared through {@link singularise}, so "recording" and
     * "recordings" are one term. Tools are deduped across cross-listed
     * categories (keeping the highest-scoring category), sorted by score then
     * name, and capped.
     *
     * The domain weight exists because the VERB is shared by dozens of tools
     * and the ENTITY is what the user actually named: before it, "delete a
     * recording" led with `pica_agreement_types_delete` and "read a document"
     * put `pica_notifications_mark_read` above every documents tool. Both are
     * pinned in `src/__tests__/tools/discovery-ranking.test.ts`, alongside the
     * flagship isrc-from-spotify pin the weighting must not disturb.
     */
    private matchDiscovery;
    /**
     * A single-line description for a match row: the first sentence of the
     * tool's description, capped to keep the shortlist scannable.
     */
    private oneLineDescription;
    private buildToolDetailsTool;
    private buildToolDetail;
    private buildExecuteTool;
}
export {};
//# sourceMappingURL=discovery.d.ts.map