import type { AgentTool } from "../internal/harness-types.js";
import type { ToolPolicy } from "./tool-policy.js";
/** The reserved tool name for AskUserQuestion — shared so the durable-resume path (runtask) can recognize it
 *  as a content-ask whose SHOWN questions must not be rewritten by an operator `updatedInput` (design/80 D-F).
 *
 *  RESERVED means the whole engine keys content-ask semantics on this name: a policy `ask` on it is NEVER
 *  routed to a synchronous PERMISSION approver — on the main gate, the degraded fall-through, and the
 *  inherited ancestor wrappers alike (a permission frame's allow/deny cannot carry an ANSWER; CC parity:
 *  AskUserQuestion never raises a permission card). What it DOES reach, in this order, is the run's own
 *  content-ask channel: a LIVE question face answers it in-stream (the gate routes the question, not a
 *  verdict), and only with no live face — or with the durable gate forced — does it park durably or answer
 *  the typed refusal. Permission-GATING this tool stays unsupported by design; content-ROUTING it is the
 *  supported path. A caller-supplied tool reusing this name inherits the same routing; don't. */
export declare const ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
/**
 * AskUserQuestion (design/64 §5). A model-callable tool that lets the agent ask the USER a structured
 * multiple-choice question (which approach? which of these?) — distinct from the design/37 permission `ask`
 * ("may I run this tool?"). The model only EMITS the call; the harness routes it to the caller via the
 * {@link OnQuestion} seam (deployment supplies a real human / UI / HITL). **LLM has zero IO** — it cannot
 * collect answers itself. Headless (no `onQuestion`) returns a typed "no human available" so the model
 * proceeds with a default instead of hanging. **design/80 D-F (answer fence):** the answer no longer assumes a
 * trusted control plane — in the Supervisor topology it arrives over the wire (an operator `/decide`), an
 * injection vector. So a `selected` value is echoed as a chosen option ONLY if it is one of the offered option
 * labels (`selected ⊆ options` — the model's own text), and the operator's free-text (`note`, plus any off-list
 * input) is wrapped in an untrusted-DATA fence so it can never be read as instructions. Abort while waiting is
 * handled by the §9 interrupt path (the pending call is closed). Mounting (CC non-interactive `isEnabled`
 * parity): only when a delivery face exists — a live `onQuestion`, or a durable park consumer (`checkpointStore`
 * + `durableApproval`/`forceDurableGate`), or an explicit `TaskSpec.interactiveTools: true` — see that knob.
 */
export interface AskQuestionOption {
    /** The choice text the user picks. */
    label: string;
    /** What this option means / its trade-off. */
    description: string;
    /** design/116 W3 (CC parity): optional preview content (mockup / code snippet / comparison) shown while this
     *  option is focused. The engine only TRANSPORTS it — it rides the {@link AskQuestionRequest} to `onQuestion`
     *  untouched; rendering (markdown box, side-by-side, …) is the shell/HITL surface's job. */
    preview?: string;
}
export interface AskQuestion {
    /** The full question. */
    question: string;
    /** A short header/label (≤ ~12 chars) for UIs. */
    header: string;
    /** 2-4 mutually-exclusive choices (unless multiSelect). */
    options: AskQuestionOption[];
    /** Allow selecting more than one option. */
    multiSelect?: boolean;
}
export interface AskQuestionRequest {
    toolCallId: string;
    /** 1-4 questions. */
    questions: AskQuestion[];
    /** Runner-filled source identity (parallel to {@link import("./tool-policy.js").AskRequest}.principal,
     *  design S1d §1): the issuing task's end-user principal (design/62), for per-user attribution by an
     *  AGGREGATING onQuestion. Worker/tool cannot set it. Undefined ⇒ none (a non-aggregating onQuestion
     *  ignores it — additive, no-op). `readonly`: Runner-populated metadata. Populated on every path that
     *  actually CALLS a question face: the in-tool synchronous call, and the gate's pre-execution content-ask
     *  routing. A durable suspend invokes no face at all, so it carries none. */
    readonly principal?: string;
    /** Runner-filled source-task identity (= issuing task's session id), for per-worker attribution by an
     *  aggregating onQuestion. **worker-unforgeable for Runner-created delegated subagents/team members**
     *  (those child specs do not pass TaskSpec.sessionId — the Runner mints the id); a top-level caller CAN
     *  still set `TaskSpec.sessionId`, so this is NOT a globally unforgeable run id (design S1d §1.3).
     *  `readonly` + same "only where a face is really called" caveat as `principal`. */
    readonly sourceTaskId?: string;
    /**
     * The canonical hash of the tool-call arguments this question was built from, as computed by the
     * engine's one `boundInputHashOf` (the same digest the durable park binds its checkpoint to, so the
     * two lanes are reconcilable against each other). Present only when the engine routed the question
     * — a directly-mounted tool calling its own face carries none.
     *
     * The split to keep straight, because the two words are easy to swap:
     *  - `(sourceTaskId, toolCallId, boundInputHash)` is the RECONCILIATION key — "is this the same
     *    question, on the same call, in the same run?". It is what lets an in-stream delivery be matched
     *    against a parked one, and what an audit trail joins on. It is NOT an identity: all three
     *    components repeat for two sequential deliveries of an identical question.
     *  - {@link deliveryId} is the IDENTITY — "which delivery attempt is this?" — and is the only one of
     *    the two a registry may key open requests on.
     * Keying a registry on the reconciliation key closes the second delivery on the first one's ticket.
     */
    readonly boundInputHash?: string;
    /**
     * The IDENTITY of this delivery attempt: engine-minted, unique per delivery, never derived from
     * anything a model or a caller supplies. This is the key for the single-winner obligation in
     * {@link OnQuestion} — a registry keys open requests on it (scoped by tenant), so a deadline expiry
     * and a UI submission for the SAME delivery resolve to exactly one winner, and a late answer is
     * refused instead of applied to a question nobody is waiting on any more.
     *
     * Why not the tuple you might reach for first: `toolCallId` comes from the model/provider and is not
     * guaranteed unique within a session, `sourceTaskId` can be caller-supplied, and the argument hash
     * is identical for two identical questions. Their combination therefore repeats for two sequential
     * deliveries of the same question — under which a registry would close the second on the first's
     * ticket, or hand the second the first's late answer. That tuple remains the RECONCILIATION key
     * (see {@link boundInputHash}); this field is the identity. One names the question, the other names
     * the attempt, and only the attempt is unique.
     *
     * Present on EVERY delivery the engine makes, by either route (the tool's own call and the gate's
     * pre-execution routing). Optional on the type only because a deployment may mount the tool itself.
     */
    readonly deliveryId?: string;
}
/** One question's answer: the selected option label(s) + an optional free-text note. */
export interface QuestionAnswerItem {
    header: string;
    selected: string[];
    note?: string;
}
export interface QuestionAnswer {
    answers: QuestionAnswerItem[];
}
/**
 * The "nobody could be reached for this one" arm of {@link OnQuestionOutcome}. Its contract is narrow and
 * load-bearing: return it ONLY when the deployment made ZERO interaction on this question — no card was
 * shown, no partial selection was collected, nothing was submitted. That is what lets the engine re-route
 * the same question (park it durably for a later human, or answer the honest "it was NOT shown" refusal)
 * without any risk of the same question being decided twice.
 *
 * A face that DID show the question and then failed (disconnect, deadline after render, adapter error)
 * must THROW instead — the two are deliberately different values with different engine handling, and
 * folding a failure into this arm is what would put one question in front of two deciders.
 */
export interface QuestionUnavailable {
    readonly kind: "unavailable";
}
/**
 * The three-valued outcome of a content-ask delivery (one value per thing that can actually happen):
 *  · a {@link QuestionAnswer} — the human answered (including "selected nothing", which IS an answer);
 *  · {@link QuestionUnavailable} — nobody was reachable and nothing was shown (see its contract);
 *  · a THROW — delivery was attempted and failed. Never collapse a throw into `unavailable`.
 */
export type OnQuestionOutcome = QuestionAnswer | QuestionUnavailable;
/** How an {@link OnQuestionOutcome} reads. `"contradictory"` covers a value claiming BOTH arms at
 *  once (the `unavailable` discriminant alongside an `answers` array — structural typing permits it, and
 *  the outcome is deployment-supplied and may cross a wire), a value claiming NEITHER arm (a failed
 *  decode must not read as "the user selected nothing"), and a value that cannot be read at all. */
export type QuestionOutcomeReading = 
/** The answer, already narrowed AND captured as an inert, density-validated clone — a caller
 *  neither re-reads the caller's value to get at it nor needs a `structuredClone` of its own. */
{
    readonly shape: "answered";
    readonly answer: QuestionAnswer;
} | {
    readonly shape: "unavailable";
} | {
    readonly shape: "contradictory";
};
/**
 * THE reader for a question outcome, and the only place its fields are touched. Total by construction:
 * every way the value can misbehave — a throwing getter, both arms at once — lands on
 * `"contradictory"`, which the callers refuse rather than resolve. That refusal is the point: reading a
 * contradictory value as unavailable would discard an answer somebody may have given AND re-route the
 * question to a second decider, while reading it as answered would deliver an answer the face may have
 * meant to withhold. Neither guess is safe, so neither is made.
 */
export declare function classifyQuestionOutcome(outcome: OnQuestionOutcome): QuestionOutcomeReading;
export declare function canonicalizeCapturedPlainData(node: unknown, seen?: WeakSet<object>): boolean;
/**
 * Narrow an {@link OnQuestionOutcome} to its no-interaction arm — the ONLY value that licenses
 * re-routing a question to a later decider, so it is read strictly (the discriminant must be present
 * and the value must not also carry answers).
 *
 * For a FORWARDING wrapper relaying this seam. The engine's own decision points do not use it: they
 * call {@link classifyQuestionOutcome} ONCE and switch on the result, because the value is
 * deployment-supplied and its fields may be getters — classifying twice lets an outcome read as
 * answered on one call and unavailable on the next, which is precisely how one question would reach
 * two deciders. A wrapper that needs more than "is this the unavailable arm" should classify once too.
 */
export declare function isQuestionUnavailable(outcome: OnQuestionOutcome): outcome is QuestionUnavailable;
/**
 * The content-ask seam (design/64 §5.2): the deployment routes an AskUserQuestion to a real human/UI and
 * returns their selection. Parallel to (and independent of) {@link import("./tool-policy.js").OnAsk} — a
 * permission ask is allow/deny, a content ask is a choice. `signal` fires on task abort: race your wait so
 * an unanswered question is released at the deadline (it does not hang the worker).
 *
 * **Single-winner obligation (the deployment's half).** A face that races a deadline against a real UI
 * owns the atomicity: before returning {@link QuestionUnavailable} it must CLOSE the open request, so a
 * submission arriving one tick later is refused rather than applied. A signal race alone is not enough —
 * two independent resolutions of one question is the shape this seam exists to prevent. Key the registry
 * on {@link AskQuestionRequest.deliveryId} (scoped by tenant) — NOT on the call id or the argument hash,
 * neither of which is unique per delivery. The engine consumes exactly one outcome per call id
 * and never asks the same call twice, so honoring this contract makes the whole path single-winner; core
 * does NOT serialize question delivery ACROSS runs (a shared UI seat is the deployment's closure), only
 * within a run — the tool executes sequentially, so one run never has two questions in flight.
 */
export type OnQuestion = (req: AskQuestionRequest, signal?: AbortSignal) => Promise<OnQuestionOutcome>;
/**
 * design/173 §8.1/§8.2 — why a synthetic self-answer instruction (the NO_HUMAN text) was returned:
 * `"seam_absent"` = no `onQuestion` face was wired at all; `"callback_failed"` = a face WAS wired
 * and threw (shell disconnect / timeout — the more common field case, and the more deceptive one:
 * the deployment believes it has this limb); `"declined_unavailable"` = a face was wired, it was
 * called, and it affirmed that nobody was reachable for this question (nothing was shown). The three
 * must never collapse — "not installed", "installed but broken" and "installed, working, and nobody
 * home" are three different operator actions.
 */
export type SyntheticContinuationReason = "seam_absent" | "callback_failed" | "declined_unavailable";
/** design/173 §8.1 — who produced the answer the model continues on. The engine never fabricates an
 *  answer: the synthetic arm returns a self-answer INSTRUCTION, hence the honest name. */
export type AskAnswerContinuationSource = "human_response" | "synthetic_self_answer_instruction";
/**
 * design/173 §8.1 — the AskUserQuestion tool card (`details.type: "ask-question"`, registered in
 * `CC_DETAIL_TYPES` — the card set is a two-way contract: mint and registration travel in the SAME
 * change). The in-band structured marker is the AUTHORITATIVE provenance signal (disclosure frames
 * can drop or reorder; a consumer must never match the NO_HUMAN prose). Two shapes:
 *  - the CONTINUATION card — every answered exit, human or synthetic (`runContinues: true` is the
 *    warning-not-error semantic: the run proceeds by design, a headless deployment must not be
 *    marked failed over it);
 *  - the INTERACTIVE-FAILURE card — the run refuses the silent synthetic continuation and reports a
 *    coded tool failure instead (`isError: true`). Two codes, kept apart for the same reason the
 *    reasons above are: `"question.human_channel_failed"` = the wired channel threw;
 *    `"question.human_unavailable"` = the channel worked and said nobody was reachable.
 */
export type AskQuestionCardDetails = {
    type: "ask-question";
    /** The question's id (= the tool call id — the same key the park/HITL faces use). */
    questionId: string;
    continuationSource: AskAnswerContinuationSource;
    runContinues: true;
    /** Present iff `continuationSource` is synthetic. */
    reason?: SyntheticContinuationReason;
} | {
    type: "ask-question";
    questionId: string;
    code: "question.human_channel_failed" | "question.human_unavailable";
    reason: "callback_failed" | "declined_unavailable";
};
/**
 * The ONE mint point for the continuation card. Every exit that continues the run on
 * an answer — human or synthetic, in the tool or on the engine's routing lane — builds its card here, so
 * a new delivery point cannot quietly reopen the silent-degrade hole by shipping without a card.
 */
export declare function askQuestionContinuationCard(questionId: string, continuationSource: AskAnswerContinuationSource, reason?: SyntheticContinuationReason): AskQuestionCardDetails;
/**
 * The ONE shape validator for a question batch, shared by the tool body and by the engine's routing lane
 * (which validates BEFORE it puts anything in front of a person — a malformed batch must be refused to
 * the model, not rendered). Returns the model-facing error text, or `undefined` when the batch is well
 * formed. Total: it never throws on untrusted field shapes.
 */
export declare function validateAskQuestions(questions: unknown): string | undefined;
/** design/173 件3 — engine-side options for the mounted tool (prepare wires them; a deployment
 *  mounting the tool directly may too). All optional: absent = pre-173 behavior. */
export interface AskUserQuestionToolOptions {
    /** The run's RESOLVED interaction posture — see {@link import("./types.js").TaskSpec.interactionPosture}
     *  for the three-leg resolution (spec > the spawning run's resolved posture > deps); prepare passes
     *  that one resolved value, the same one the posture door reads.
     *  Under `"interactive"` BOTH no-answer arms of a wired channel are coded tool FAILURES instead of a
     *  silent synthetic continuation — a channel that THROWS returns `question.human_channel_failed`, one
     *  that reports nobody reachable returns `question.human_unavailable` — because an interactive
     *  deployment must face both. `"headless"`/absent keep the existing degrade. */
    posture?: "interactive" | "headless";
    /** The EXPLICIT knob (default off) letting posture `"interactive"` continue with the synthetic
     *  self-answer instruction after all, on BOTH hardened arms (channel threw / nobody reachable);
     *  disclosure still minted. */
    interactiveFallback?: boolean;
    /** Engine sink for the synthetic-continuation disclosure (the onError parallel leg — phase
     *  `"degraded"`, classification `"no-human-autoanswered"`). Deduped **per ask-call** (the dedup key
     *  is the tool-call id; one call may carry 1–4 questions and still discloses ONCE — "per question"
     *  would misread against that array), not per run: a second ask-call in the same run still
     *  discloses. The suppression set lives for ONE prepare (one leg): a continuation leg re-discloses
     *  the same call id — deliberate, or the second leg's host would be blind; a host aggregating
     *  across legs dedups on (sessionId, toolCallId) itself. Swallow-guarded — an observer bug never
     *  faults the ask. */
    onSyntheticContinuation?: (info: {
        questionId: string;
        reason: SyntheticContinuationReason;
    }) => void;
    /**
     * The call id of the ONE pending question this leg is redeeming an operator's approval for. An
     * approval was spent to get that question executed, so an `unavailable` outcome on THAT call must not
     * be absorbed into the synthetic self-answer: the operator has to learn their decision bought a
     * question nobody answered, and it becomes the coded failure `question.human_unavailable` instead.
     * Scoped to the id rather than to the leg — a NEW question raised later on the same leg had no
     * approval spent on it and keeps the ordinary continuation. (A THROW keeps its own posture-driven
     * handling on every call; that arm is unchanged.)
     */
    redeemedApprovalCallId?: string;
    /** Digest of the question batch that approval was granted for, paired with
     *  {@link redeemedApprovalCallId}. Call ids are provider-supplied and can repeat, so the id alone
     *  would let a LATER question inherit the claim that an operator approved it. */
    redeemedApprovalQuestionsHash?: string;
}
export declare function createDurableQuestionPolicy(): ToolPolicy;
/**
 * Placeholder {@link OnQuestion} that mounts the AskUserQuestion tool on a worker with NO live human
 * (the durable-ask suspend side, TC-5.4). It must never actually run — the durable-question policy
 * suspends the call before execution — so reaching it means the wiring is missing the policy/store;
 * it throws a configuration error rather than silently telling the model "no human is available".
 */
export declare const QUESTION_AWAITS_RESUME: OnQuestion;
/**
 * Brand a face that can answer exactly ONE pre-bound call and nothing else — the engine builds one
 * when a resume carries an operator's answer for the checkpointed question. It is a real face for that
 * one call id and a dead end for every other question, which is why it must not be mistaken for a run's
 * live question channel: routing a NEW question to it would turn "ask a human" into a guaranteed
 * failure on a leg where the honest outcome is to park the new question for its own decision.
 * Returns the same function, so it can wrap the construction site inline.
 */
export declare function markBoundOnlyQuestionFace<F extends OnQuestion>(face: F): F;
/**
 * THE named predicate for "this run has a question face that can actually answer a question put to it",
 * shared by every reader (the gate's routing decision, the tool mount, the wiring manifest's
 * `question.wired`, and the resume guard). One derivation, because the ways of getting it wrong point in
 * different directions and all of them are silent:
 *  · a non-function seat value (a JS caller passing junk) is not a face at all;
 *  · {@link QUESTION_AWAITS_RESUME} IS a function, and IS the documented standard assembly of the durable
 *    topology — but its whole contract is that it must never run. Counting it as live would route real
 *    questions into a configuration error instead of the park the deployment built;
 *  · a {@link markBoundOnlyQuestionFace} face answers one already-decided call and refuses the rest.
 * Callers resolve the seat first (`spec.onQuestion ?? deps.onQuestion`) and pass the resolved value.
 */
export declare function isLiveQuestionFace(face: unknown): face is OnQuestion;
/** Build the AskUserQuestion tool. When `onQuestion` is undefined the tool still mounts but returns the
 *  headless default (so a run started without a human in the loop never hangs). */
export declare function createAskUserQuestionTool(onQuestion?: OnQuestion, 
/** design S1d §1.2: Runner-filled source identity, merged into the {@link AskQuestionRequest} so an
 *  aggregating onQuestion can attribute per worker/user. Omitted ⇒ fields stay undefined (no-op). */
source?: {
    principal?: string;
    sourceTaskId?: string;
}, opts?: AskUserQuestionToolOptions): AgentTool;
/**
 * The ONE producer of the "engine stripped the spawn turn's question face" fact. The strip (removing `onQuestion`
 * from a child spec — or from the parent SPEC seat a child is built from — when the child OUTLIVES the request that
 * spawned it) and the disclosure flag (`RunInternals.questionFaceStripped` → the wiring manifest's
 * `question.wired: "stripped_bg_lane"` + the posture door's exemption) are PAIR-PRODUCED from this single expression —
 * never inferred from spec shape, never minted anywhere else. Three families ride it: the Agent tool's session-scoped
 * background lanes (spec-override form), its retain snapshots (destructure-drop form; the flag then rides
 * `internalsSnapshot`, so a revived leg keeps the exemption), and the Workflow tool (submit-and-return: every agent it
 * spawns outlives the launching request, so the host's seat is stripped before it reaches the launch site). WHY:
 * `spec.onQuestion` is by contract a per-REQUEST lane torn down with its turn; handing it to a leg that runs hours later
 * would strand the child asking into a closed surface (`AskUserQuestion` awaits the answer). A key REMOVED and a key set
 * `undefined` resolve identically through the engine's `spec.onQuestion ?? deps.onQuestion` reads — a process-level
 * `RunnerDeps.onQuestion` still reaches the child; only the turn-bound value drops. Trust posture: RunInternals is a
 * PUBLIC trusted-caller parameter, so the flag is an HONESTY device (the engine disclosing its own strip), not a
 * security gate. The flag states "a face WAS removed here" — minted only when one actually existed (an inherently
 * faceless lane reads `absent`, not `stripped_bg_lane`, and collects no exemption).
 */
export declare function stripSpawnTurnQuestionFace<S extends {
    onQuestion?: OnQuestion;
}>(spec: S): {
    spec: Omit<S, "onQuestion">;
    internalsFlag: {
        questionFaceStripped?: true;
    };
};
