/**
 * Tools slot subflow builder
 *
 * Pattern: Builder (returns a FlowChart mountable via addSubFlowChartNext).
 * Role:    Layer-3 context engineering. Resolves the tools list the LLM
 *          sees on this iteration — one InjectionRecord per exposed tool.
 * Emits:   None directly; ContextRecorder sees the writes.
 *
 * Minimal scope for Phase 3e: static tool registry, all exposed every
 * iteration. Full permission gating / skill activation / context-aware
 * tool filtering arrives in Phase 5.
 */
import type { FlowChart } from 'footprintjs';
import type { LLMToolSchema } from '../../provider-contract/types.js';
import { type StepPlanFor } from '../../injection-engine/skillSteps.js';
import type { Tool } from '../../tools/tools.js';
import type { ToolClaim } from '../agent/buildToolRegistry.js';
import type { ToolNameChannel } from '../../events/payloads.js';
import type { ToolProvider } from '../../tools/toolProvider.js';
import type { Classifier } from '../../classify/types.js';
import type { ZoneName } from '../../time/zone.js';
/**
 * Mutable cache shared between `buildToolsSlot` (writer) and
 * `buildToolCallsHandler` (reader) within ONE run. The Tools slot
 * resolves the provider's tools each iteration and stashes the
 * Tool[] here; the toolCalls handler reads on dispatch — so async
 * providers pay the discovery cost once, not twice. Scoped to the
 * chart build so concurrent `agent.run()` calls each get their own
 * cache.
 */
export interface ProviderToolCache {
    current: readonly Tool[];
}
/**
 * A party that put a tool name forward for the wire (9.92.0) — the vocabulary
 * `tools.shadowed` and `tools.claim_swallowed` speak. `id` is the provider id or
 * the skill id when the party has one; a static `.tool()` and a framework
 * auto-attach carry none.
 */
export interface ToolParty {
    readonly channel: ToolNameChannel;
    readonly id?: string;
}
/**
 * WHO PUT EACH NAME ON THIS EPOCH'S WIRE (9.92.0) — written by Compose from
 * the same merge that produces `scope.toolSchemas`, read by the tool-calls
 * handler's `lookupTool` so that DISPATCH FOLLOWS THE OFFER: a name the model
 * was served resolves to the implementation of the party whose contract it
 * read, never to whichever map happened to be consulted first.
 *
 * The `ProviderToolCache` pattern, and for the same reason: build-closure
 * plumbing shared by the slot (writer) and the handler (reader) within one
 * chart, never scope state — so a run with no collision commits the exact
 * bytes it always did. Overwritten every iteration; a name absent from it was
 * not on the wire this epoch and dispatch falls back to the build-time map,
 * which is the capability law's held-out clause unchanged.
 */
export interface ServedToolParties {
    current: ReadonlyMap<string, ToolParty>;
    /**
     * The party each name was LAST served under, across the epochs of one run
     * (cleared on iteration 1). The off-wire fallback reads it: a name that
     * left the wire may still be answered by the party the model last read it
     * under — never by a party the model was never shown under that name.
     */
    readonly lastServed: Map<string, ToolParty>;
}
/** One schema put forward for the wire, by whom — and WHICH implementation. */
interface WireCandidate {
    readonly schema: LLMToolSchema;
    readonly party: ToolParty;
    /**
     * The implementation behind the contract. Identity is by implementation:
     * two skills sharing ONE `Tool` reference (documented-legal) are one claim
     * however many routes it arrives by, and draw nothing. Absent only on a
     * chart with no claimant record (the message-API charts).
     */
    readonly tool?: Tool;
}
/**
 * THE WIRE MERGE (9.92.0) — first occurrence wins, and the record of who won
 * and who lost is produced by the SAME pass that produces the list, so the two
 * cannot disagree about a name.
 *
 * Order is the caller's: `[static, provider, skill, step]`. A `registry`
 * candidate is the static list; a `provider` candidate the resolved
 * `ToolProvider` list; a `skill` candidate an active skill's `inject.tools`;
 * a `framework` candidate the step tool. Two candidates from one provider
 * under one name (a composed provider serving a name twice) lose to each
 * other by the same rule and are reported the same way. The SAME
 * implementation put forward twice — an always-visible skill's tool rides the
 * static list and the active-injection list; two skills may share one `Tool`
 * reference — is one claim and draws nothing: identity is by `tool`, and only
 * where no implementation is known (a chart without a claimant record) by
 * party + contract.
 *
 * @example
 * ```ts
 * const { merged, winners, losers } = mergeWire([
 *   { schema: { name: 'x', description: 'a' }, party: { channel: 'provider', id: 'p' } },
 *   { schema: { name: 'x', description: 'b' }, party: { channel: 'skill', id: 's' } },
 * ]);
 * merged.map((t) => t.description);            // ['a']
 * winners.get('x');                             // { channel: 'provider', id: 'p' }
 * losers.get('x');                              // [{ channel: 'skill', id: 's' }]
 * ```
 */
export declare function mergeWire(candidates: readonly WireCandidate[]): {
    readonly merged: readonly LLMToolSchema[];
    readonly winners: ReadonlyMap<string, ToolParty>;
    /** Every candidate that lost its name, in candidate order — competing losers. */
    readonly losers: ReadonlyMap<string, readonly ToolParty[]>;
    /**
     * The winning IMPLEMENTATION per served name, where the candidate carried one
     * (honesty layer 2): the inputs layer decorates a served schema from the
     * rules of the tool that will answer it. A name with no known tool (a chart
     * with no claimant record) is absent — and is not decorated; the dispatch
     * re-read is its guard.
     */
    readonly winningTools: ReadonlyMap<string, Tool>;
};
export interface ToolsSlotConfig {
    /** Tool registry exposed to the LLM. Empty → empty slot (LLMCall case). */
    readonly tools: readonly LLMToolSchema[];
    /**
     * Registration-time owner stamps by tool name (9.60.0) — the identity
     * edges `Tool.owner` declared. The record then attributes a registry
     * tool to its OWNING subsystem instead of deriving `source:'registry'`.
     * Absent or unmatched → exactly today's bytes.
     */
    readonly toolOwners?: ReadonlyMap<string, import('../../tools/tools.js').ToolOwner>;
    /**
     * The mount kernel's map cards (9.60.0) — id + owned tool names, for the
     * compose-seam integrity backstop below. Present only when `.maps()` is
     * mounted; absent → the backstop never runs, byte-identical.
     */
    /**
     * The per-run disposition ledger, by REFERENCE (9.60.0) — the
     * ProviderToolCache pattern: build-closure plumbing, never scope state.
     * The compose backstop notes one disposition per pass here.
     */
    readonly integrityLedger?: {
        current: import('../../integrity/disposition/ledger.js').DispositionLedger | undefined;
    };
    readonly mountedMaps?: ReadonlyArray<{
        readonly id: string;
        readonly toolNames: readonly string[];
    }>;
    /**
     * Optional `ToolProvider` consulted PER-ITERATION (Block A5 follow-up).
     * When set, the slot calls `provider.list(ctx)` each iteration with
     * the current `{ iteration, activeSkillId, identity, signal }`.
     * Provider-supplied tool schemas are MERGED with the static `tools`
     * registry — both flow to the LLM. This is what makes Dynamic ReAct's
     * tool list reshape per iteration.
     */
    readonly toolProvider?: ToolProvider;
    /**
     * The identity the CALLER named for the run this scope belongs to — the same
     * fact a tool's `ctx.identity` carries — or `undefined` when the run's
     * identity was derived. An ACCESSOR: the chart is built once and the caller
     * changes per run. Fills `ToolDispatchContext.callerIdentity`; absent → the
     * field is never set, byte-identical.
     */
    readonly callerIdentity?: (scope: object) => {
        tenant?: string;
        principal?: string;
        conversationId: string;
    } | undefined;
    /**
     * Mutable cache the slot writes to after resolving `toolProvider.list(ctx)`.
     * The same cache reference is passed to `buildToolCallsHandler` so
     * dispatch reads from this iteration's resolved Tool[] instead of
     * calling `list()` a second time. Required when `toolProvider` is set.
     */
    readonly providerToolCache?: ProviderToolCache;
    /**
     * Every build-time claimant per tool name (9.92.0) — `buildToolRegistry`'s
     * `toolClaimants`. Read for two things: the party a STATIC-list schema
     * belongs to (a `.tool()`, a framework auto-attach, an always-visible
     * skill's tool), and the claimants that never reached this epoch's merge
     * (a scoped skill's tool while the skill is inactive, a held-out step tool,
     * the framework's `skip_step` between tenures) so a dead claim can be named.
     * Absent — the message-API charts — and every static schema is `registry`.
     */
    readonly toolClaimants?: ReadonlyMap<string, readonly ToolClaim[]>;
    /**
     * The record of who put each name on the wire (9.92.0), written here every
     * iteration and read by dispatch. Same closure-shared shape and lifetime as
     * `providerToolCache`. Absent → the record is not kept, and dispatch reads
     * its build-time map first as it did before 9.92.0.
     */
    readonly servedTools?: ServedToolParties;
    /**
     * Rebuild `read_skill`'s SCHEMA for this iteration's cursor (8.5.0).
     *
     * Set only for a `.skillGraph()` agent in a per-iteration ReAct mode. The tool's
     * enum is the full catalog and never changes; what varies is the DESCRIPTION —
     * which ids the gate will actually grant from where the cursor stands. Without it
     * the menu advertised every registered skill on every iteration while the gate
     * admitted a subset, so the model was routinely offered ids it would be refused.
     *
     * Substituted by NAME in Compose, so dispatch is untouched: the tool-calls handler
     * resolves executables from `registryByName`, never from the schema list.
     */
    readonly readSkillFor?: (args: {
        readonly currentSkillId?: string;
        readonly hiddenSkillIds?: readonly string[];
        /** The turn-start MENU (SG-C), passed only while the turn's menu verdict
         *  is outstanding — see the Compose stage. */
        readonly menu?: {
            readonly candidates: ReadonlyArray<{
                readonly id: string;
                readonly relevance?: number;
            }>;
            readonly cursorId?: string;
            readonly stay?: boolean;
        };
    }) => LLMToolSchema | undefined;
    /** Budget cap (chars). Default: 2000. Set from the public door as
     *  `contextBudget.tools` on `AgentOptions`. */
    readonly budgetCap?: number;
    /**
     * The frozen step plans, keyed by skill id (9.18.0) — set only when ≥1
     * registered skill declares `steps`. While a stepped tenure is active and
     * unfinished (`scope.stepPointer`, threaded by the mount mappers), Compose
     * narrows the OFFER to the current step: the stepped skill's own tools
     * that are not the current step's tool are held out of the request, the
     * current step's tool description leads with the `[Step k of n — <note>]`
     * banner (a rebuilt schema copy substituted BY NAME — the `read_skill`
     * precedent, so dispatch is untouched), and `skip_step` is appended.
     *
     * The escape hatches STAY OFFERED (house law): `read_skill`,
     * `list_skills`, every OTHER active skill's tools, the baseline `.tool()`
     * registry, provider tools. The hold-out applies only to names whose SOLE
     * active owner is the stepped skill — a name another active source also
     * carries (the same Tool reference shared across skills, a provider) is
     * somebody's escape hatch and is never pulled; a baseline `.tool()` can
     * never even collide (that overlap is refused at Agent build). Absent
     * plan / absent pointer / complete procedure → this path is
     * byte-identical to today.
     */
    readonly stepPlanFor?: StepPlanFor;
    /**
     * THE FINDINGS LEDGER IS ARMED (9.101.0, `.findings()`) — present ONLY
     * then, and only ever `true`. When present, every schema on the committed
     * wire list gains the reserved optional `_findings` property
     * (`findings/reserved.ts · withFindingsArgument`) at the ONE decoration
     * site: after `mergeWire`, before `scope.toolSchemas` is written. Absent —
     * the default — and the committed list is the merged list, byte for byte.
     *
     * The decoration is stable: mutable result IDs are served in the call's
     * request-only DATA, outside this slot and its cached tool contract.
     */
    readonly findings?: true;
    /**
     * THE INPUTS LAYER IS ARMED (honesty layer 2) — present ONLY then, only
     * ever `true`. Every served schema whose WINNING implementation declares
     * `askOrAssume` is rebuilt at the ONE decoration site, one step before
     * `withFindingsArgument` (`arguments/serve.ts` · `withArgumentRules`): each
     * `assume`-ruled argument leaves `required` and its property says the rule.
     * Absent, nothing is decorated — a ruled tool on an agent without the layer
     * is refused at dispatch, and a sentence promising a fill would be false.
     */
    readonly inputsLayer?: true;
    /**
     * DECLARED SOURCES ARE ARMED (honesty layer 2, `.findings({ argumentSources:
     * true })` or `.inputsLayer({ argumentSources: true })`) — present ONLY
     * then, only ever `true`, and only beside `inputsLayer`. At the same
     * decoration site, a schema whose WINNING implementation carries argument
     * rules (`tools/declare.ts` · `carriesRules`) gets `from`: beside
     * `findings`, the ledger's `_findings` variant with `from`
     * (`findings/reserved.ts` · `withFindingsArgument`'s `from`); without it,
     * the reserved argument with `from` alone (`withSourcesArgument`). An `ask`
     * rule's sentence also names `_findings.from` (`arguments/serve.ts` ·
     * `ASK_SOURCES_SENTENCE`). Every other schema keeps its decoration — the
     * ledger's base by reference, or none. Absent → the decoration it always was.
     */
    readonly argumentSources?: true;
    /**
     * THE TIME LAYER IS ARMED (`.time()`) — present ONLY then. At the same
     * decoration site the slot composes the ONE
     * served time line from the tools it really serves — first the run clock
     * (`agent/arguments/serve.ts` · `clockSentence`, every request, G16; mount
     * arg `timeClock`), then (`timeWindowsLine`) each window the reader SETTLED this turn in
     * each period tool's own form, each quote still pending, and the window the
     * person set in the app's time control (`time.window`; on every request,
     * with or without a period tool served, and with or without `inputsLayer`
     * — a fact about the person's turn, G14) — and writes it to
     * `timeLine` as `{ iteration, text }`; the mount carries it to `callLLM`,
     * which appends it LAST to the request, never to history. The tool schemas
     * are not touched. Reads two mount args under this gate only, `timeClock`
     * (`agent/buildAgentChart.ts` · `timeClockArg`) and `timeWindows`
     * (`time/windows.ts` · `readerWindowsOf` — absent on a turn with none).
     * `appZone` is the app's `.time({ zone })`. `reader` — the reader is armed
     * (`.time({ reader })`, step T6b) beside `inputsLayer`: the line is written EVERY composition
     * (`''` when there is nothing to say), so a slot that does not re-run never
     * serves a stale one; without it only a line with something to say is
     * written (the control window alone).
     */
    readonly timeWindows?: {
        readonly appZone?: ZoneName;
        readonly reader?: true;
    };
    /**
     * THE TIME LAYER IS ARMED (`.time()`, step T8) — present ONLY then, and only
     * beside `inputsLayer`. The same site appends the turn's time limits
     * (`agent/arguments/serve.ts` · `timeLimitsLine`, which renders them) to the
     * ONE served time line, from one mount arg, `timeLimits` — unrendered facts
     * (`coverage/timeLimitFacts.ts` · `timeLimitFactsOf` — absent on a turn
     * whose reads match what was asked).
     * Without the reader the line is written only when it says something; the
     * iteration stamp keeps an older one from being served.
     */
    readonly timeLimits?: true;
    /**
     * TOOL CHOICE BY CLASSIFIER IS ARMED (9.105.0, `.toolChoice()`) — present
     * ONLY then. Compose then asks `classifier` which of the merged wire's
     * tools (minus the doors) answers the current step, files the pick under
     * `toolChoices` BEFORE `scope.toolSchemas` is written, and — under `top` —
     * commits the top-N plus the doors as the served list, at the ONE
     * decoration site, so the receipt hashes and `servedView` rebuilds the
     * narrowed list by construction. The pick is awaited: the stage returns a
     * promise under this gate and stays synchronous without it. Reads three
     * mount args under this gate only: `userMessage`, `priorToolChoices` (the
     * parent's rows, a frozen input the slot never writes), `wrapUpAsked` and
     * `toolsOffAfterRevision`.
     * The module that asks is loaded through `import()` — an unarmed agent
     * never loads it.
     */
    readonly toolChoice?: {
        readonly classifier: Classifier;
        /** Serve the top-N plus the doors; absent = advisory only (the full wire). */
        readonly top?: number;
        readonly alwaysServe?: readonly string[];
    };
}
/**
 * Build the Tools slot subflow.
 *
 * Mount with:
 *   builder.addSubFlowChartNext(SUBFLOW_IDS.TOOLS, buildToolsSlot(cfg), 'Tools', {
 *     inputMapper: (parent) => ({ iteration: parent.iteration }),
 *     outputMapper: (sf) => ({ toolsInjections: sf.toolsInjections, toolSchemas: sf.toolSchemas }),
 *   })
 */
export declare function buildToolsSlot(config: ToolsSlotConfig): FlowChart;
export {};
