/**
 * A set a model-facing sentence is allowed to NAME, after the role filter has
 * run over it (9.86.0) — and the one fact a filtered list cannot be written
 * without.
 *
 * ── OMIT, NEVER DENY ────────────────────────────────────────────────────
 *
 * A narrowing may take a name off a sentence. It may not turn that name's
 * absence into a claim that the thing is gone: the graph, the dispatch map and
 * the menu are all still holding it, and a model told "no skill was reachable"
 * stops asking for the one it may not be told about. Omission costs nothing and
 * is always true; the negative sentence is a denial of what the run holds.
 *
 * Every composer took plain arrays and branched on `length > 0`, so "the set
 * was empty" and "the filter emptied it" produced the SAME sentence — one
 * shape, two facts, in three separate places. `held` is that missing fact, and
 * it is REQUIRED rather than optional on purpose: the compiler now asks every
 * caller the question the call sites each forgot to answer.
 *
 * ── WHY THIS IS A LEAF AND NOT A HELPER IN THE GATE ─────────────────────
 *
 * It lived in `core/agent/stages/toolCalls.ts`, which put it on the wrong side
 * of the skill-graph fence: `read_skill`'s DESCRIPTION is composed inside
 * `src/injection-engine/`, which may not import the agent loop, so the one
 * surface the model reads to CHOOSE a skill could not use the fact its own
 * refusals were being repaired with — and it went on printing "Nothing is
 * reachable from here" over a graph whose only hop the role filter had removed.
 * The same reason `src/lib/saidByPerson.ts` is a leaf: the writer and the
 * recogniser share one rule, so they cannot drift.
 *
 * @example
 * ```ts
 * const hidden = new Set(['beta']);
 * const hops = spoken(['beta'], (id) => !hidden.has(id));
 * // → { named: [], held: true } — the clause is DROPPED, not negated.
 * ```
 */
export interface SpokenIds {
    /** The members this sentence may name — what survived the filter. */
    readonly named: readonly string[];
    /** Did the UNFILTERED set hold anything? `true` with an empty `named` is the
     *  filtered-to-empty case: the clause is omitted rather than negated. */
    readonly held: boolean;
}
/**
 * Apply the role filter to one set and keep both halves (9.86.0).
 *
 * `mayName` is the caller's visibility predicate — skill ids test membership of
 * `scope.hiddenSkillIds`, tool names test whether every skill declaring them is
 * hidden. Both questions have one answer shape, and this is it.
 */
export declare function spoken(all: readonly string[], mayName: (id: string) => boolean): SpokenIds;
