import type { RunnerSelfSeat } from "../core/runner/contracts.js";
import type { McpServerSpec, ModelRef, ModelRole, TaskSpec, ToolSpec } from "../core/types.js";
import type { CheckpointGate, CheckpointToken } from "../core/checkpoint-store.js";
export interface TeamMember {
    /** The member's role / specialty, e.g. "安全评审" or "performance". */
    role: string;
    /** Optional per-member model (falls back to the team default). */
    model?: ModelRef;
    /** Optional per-member model **role** (when `model` is omitted) — for per-member cost tiering, e.g.
     *  a "researcher" on `"subagent"` (cheap) and a "critic" on `"default"`. Default `"team"`. */
    modelRole?: ModelRole;
    /** Optional persona / standing instructions for this member. */
    systemPrompt?: string;
    /**
     * Tools this member may use mid-debate to **ground or verify** a claim against the real artifacts
     * (e.g. a read-only repo tool) instead of speculating — tool-augmented debate (the "Tool-MAD"
     * pattern). Falls back to the team's shared {@link TeamDiscussionOptions.tools}. A member's turn
     * becomes a full agent loop, so it is bounded by `limits` (and core's default turn cap).
     */
    tools?: ToolSpec[];
    /**
     * Per-member tool allow/deny (design/38 1A), applied to this member's resolved tools (own or the
     * team default): `allowTools` keeps only the named tools (`["*"]` = all), then `denyTools` removes.
     * Lets a "researcher" be read-only and an "implementer" carry write tools without separate roles.
     */
    allowTools?: string[];
    denyTools?: string[];
    /** Task-scoped MCP servers for this member; falls back to the team's shared `mcp`. */
    mcp?: McpServerSpec[];
}
export interface TeamTurn {
    round: number;
    role: string;
    text: string;
    /** True when this member turn failed (retries exhausted). Surfaced in the result for visibility,
     *  but **excluded from every prompt** so an `[unavailable]` marker can't pollute the discussion. */
    failed?: boolean;
}
export type TeamEvent = {
    type: "member_start";
    round: number;
    role: string;
} | {
    type: "member_end";
    round: number;
    role: string;
    text: string;
}
/** budget axes: the cumulative team budget was exhausted after this member's turn settled —
 *  remaining rounds/members are skipped and the discussion goes straight to synthesis. */
 | {
    type: "budget_stop";
    axis: "maxTokens" | "maxCostUsd";
    round: number;
    role: string;
} | {
    type: "synthesis_start";
} | {
    type: "done";
};
export interface TeamDiscussionOptions {
    runner: RunnerSelfSeat;
    /** Default model for members / synthesizer without their own. If omitted, members resolve the
     *  `team` role and the synthesizer the `synthesize` role (both fall back to `default`). */
    model?: ModelRef;
    /** The participants. */
    members: TeamMember[];
    /** What the team is discussing / deciding. */
    topic: string;
    /** Number of discussion rounds before synthesis. Default 2. */
    rounds?: number;
    /**
     * Cap on the shared transcript embedded in each prompt. When the running transcript exceeds
     * this many estimated tokens, the oldest statements are summarized into a running summary
     * (keeping prompts bounded over many rounds). Omit to disable.
     */
    maxTranscriptTokens?: number;
    /**
     * Default tools for members that don't declare their own — e.g. a read-only repo tool so the
     * debate can **verify claims against the real artifacts** rather than argue over unseen code.
     * Members without tools simply reason over the shared transcript (the convergence layer).
     */
    tools?: ToolSpec[];
    /** Default MCP servers for members without their own. */
    mcp?: McpServerSpec[];
    /**
     * Who synthesizes the conclusion (default: a neutral facilitator on the default model). The
     * synthesizer may carry its own `tools`/`mcp` to spot-check a disputed point before deciding; by
     * default it has none (it converges over the transcript, it doesn't re-explore).
     */
    synthesizer?: {
        role?: string;
        model?: ModelRef;
        modelRole?: ModelRole;
        systemPrompt?: string;
        /** Tools specific to the synthesizer; does NOT fall back to the team-level `opts.tools`. */
        tools?: ToolSpec[];
        /** Per-agent tool allow/deny (design/38 1A), applied to the synthesizer's `tools`. */
        allowTools?: string[];
        denyTools?: string[];
        mcp?: McpServerSpec[];
    };
    /**
     * Per-model auth — MIRRORS {@link TaskSpec.getApiKeyAndHeaders} (forwarded into every member /
     * summary / synthesizer run, which resolve it against their own resolved models). Without this
     * seat a team whose member/synthesizer models live on per-model-credential routes had NO way to
     * carry the resolver: every nested run fell to the brain's construction-time credential, which the
     * key↔URL pairing gate refuses off the deployment root — the teacher/verify delegation family
     * carries the same seat for the same reason.
     */
    getApiKeyAndHeaders?: TaskSpec["getApiKeyAndHeaders"];
    /** Progress callback. */
    onEvent?: (e: TeamEvent) => void;
    /**
     * `maxWalltimeMs`/`maxTurns` are PER-RUN caps forwarded to every member/summary/synthesizer run.
     *
     * `maxTokens`/`maxCostUsd` (CollabTemplate.budget mid-flight enforcement) are CUMULATIVE
     * team budgets over member + summary + synthesizer spend (nested/delegated spend included, same
     * coordinate as the `stats` totals). Enforcement is checked after each member turn settles and is
     * booked — the crossing member is never killed in flight — and once a budget is exhausted
     * (running total ≥ budget) no further member turn or round is dispatched. The transcript produced
     * so far still goes through the NORMAL synthesis close-out (money already spent must yield a
     * conclusion), so the synthesizer always runs; its spend is booked but not gated. Attribution
     * lands in {@link TeamResult.budgetStop} plus a `budget_stop` event. `maxCostUsd` is compared in
     * integer micro-USD (the engine's `stats.costMicroUsd` coordinate — no float equality); runs that
     * report no cost (no `model.cost`/pricing) book 0 toward it. Both optional — omitted ⇒ prior
     * behavior byte-for-byte. The budget axes are NOT forwarded to the nested member specs (a
     * member's own per-task budget is `TaskSpec.maxTokens`/`maxCostUsd`, a different contract).
     */
    limits?: {
        maxWalltimeMs?: number;
        maxTurns?: number;
        maxTokens?: number;
        maxCostUsd?: number;
    };
    /**
     * External cancellation. When it aborts, in-flight member/synthesizer/summary runs are cancelled
     * (each member turn is a nested `runTask` given this signal) — so a team abort cascades to members
     * instead of each running to its own `timeoutSec`. Pass a parent tool's `ToolExecuteContext.signal`.
     */
    signal?: AbortSignal;
    /**
     * Authenticated end-user {@link TaskSpec.principal} (design/62) propagated to every member / summarizer /
     * synthesizer run, so their MCP tools carry the per-user identity. The orchestrator sets it from the trusted
     * task context (a team is a control-plane composition, so it is set here rather than auto-inherited).
     */
    principal?: string;
}
export interface TeamResult {
    /**
     * design/80 D-G data contract: the UNIFORM orchestrator-outcome projection. `team` was the only
     * orchestrator exit whose result didn't expose `status`/`errorCode`/`result` (cascade/teacher/verify
     * all `extends TaskResult`), forcing aggregators onto a special-case code path. These three projection
     * fields close that gap so a consumer can read the SAME failure-class fields off every orchestrator exit
     * (e.g. `errorClassOf(r.errorCode)`).
     *
     * This is a PROJECTION, not `extends TaskResult`: a team is a multi-member discussion with **no single**
     * `taskId`/`sessionId`/`stats` shape (its `stats` is `{tokens,turns}` only, and the rich per-task
     * `costMicroUsd`/`nested`/`humanReview` lines have no team-level meaning), so forcing those required
     * TaskResult fields would invent semantically-wrong values. We project ONLY the three fields aggregators
     * read. All are purely ADDITIVE — existing consumers (`conclusion`/`conclusionValid`/`transcript`/`stats`)
     * are untouched.
     *
     * `status` is `"completed"` on a valid synthesis, `"failed"` when synthesis failed or a member durably
     * paused; `result` mirrors `conclusion`; `errorCode` is set only on the durable-pause exit
     * (`unexpected.suspended` / `unexpected.needs_review`, the same hard-boundary codes the other
     * orchestrators use via {@link mapNestedSuspend}).
     */
    status: "completed" | "failed";
    /** Mirrors {@link conclusion} — the TaskResult-shaped `result` accessor for uniform aggregation. */
    result: string;
    /** The failure CLASS code (foldable with `errorClassOf`); set only when a member durably paused
     *  (`unexpected.suspended` / `unexpected.needs_review`). Undefined on a normal completion/synthesis fail. */
    errorCode?: string;
    conclusion: string;
    /** False when the synthesizer failed and `conclusion` is an `[unavailable…]` marker, not a real
     *  conclusion — so callers can tell a junk conclusion from a legitimate one. */
    conclusionValid: boolean;
    transcript: TeamTurn[];
    /** `costMicroUsd`: cumulative team LLM spend in integer micro-USD (member + summary +
     *  synthesizer, nested included) — the same engine coordinate as `TaskResult.stats.costMicroUsd`.
     *  Always set (0 when no run reported cost); optional only for type-level back-compat. */
    stats: {
        tokens: number;
        turns: number;
        costMicroUsd?: number;
    };
    /**
     * budget-stop attribution: set when a cumulative budget axis was exhausted and the
     * discussion stopped dispatching further members/rounds early. `round`/`role`/`memberIndex`
     * identify the LAST member turn that ran (the one whose settled totals crossed the budget);
     * everything scheduled after it was skipped and the transcript went straight to synthesis.
     * NOT set when the budget was only crossed by the final scheduled member turn (nothing was
     * skipped) or by the synthesizer itself — compare `stats` against the budget for that readout.
     */
    budgetStop?: {
        axis: "maxTokens" | "maxCostUsd";
        round: number;
        role: string;
        memberIndex: number;
    };
    /** Number of member/synth turns that failed even after a retry (surfaced, not silent). */
    failures: number;
    /** How many times the shared transcript was summarized to stay under maxTranscriptTokens. */
    transcriptCompactions: number;
    /** design/80 D-B: set when a member durably PAUSED (suspended/needs_review) on a HITL gate —
     *  the discussion STOPS (no synthesis on a half-done team) and surfaces the resume capability so the caller
     *  can resume the paused member via the token, then re-run. `conclusionValid` is false in this case. */
    durablePause?: boolean;
    checkpointToken?: CheckpointToken;
    checkpointGate?: CheckpointGate;
}
/**
 * Run a multi-round discussion across several role-specialized agents, then synthesize a conclusion.
 *
 * C-R14 note: the EXPORTED identifiers of this family still spell `Team*` (`runTeamDiscussion`,
 * `TeamDiscussionOptions`, `TeamMember`, …) and `ModelRole` still carries the `"team"` value. Renaming
 * them removes public exports / a union member, i.e. a BREAKING change that belongs to its own major
 * window — deliberately NOT smuggled into the vocabulary rename. The model-facing text and the framing
 * tags this file WRITES already say "discussion".
 *
 * Each member is shown the shared transcript (quoted as data) plus an explicit instruction block stating
 * the discussion context, its role, and who else is present. Members are stateless per round (the shared
 * transcript carries continuity), which keeps the orchestration simple and isolated.
 */
export declare function runTeamDiscussion(opts: TeamDiscussionOptions): Promise<TeamResult>;
