/**
 * Phase 0 keystone — `botmux dispatch` pure core.
 *
 * The orchestrator (主 bot) splits a big project into sub-projects and assigns
 * each to a small group of bots (often a coder + a reviewer). To open a
 * sub-project it seeds a fresh Lark thread and @-mentions the assigned bots so
 * each spawns its own thread-scoped session (botmux's existing one-thread-one-
 * session routing; bot→bot @ inside a thread is ungated — see
 * event-dispatcher.ts decideRouting + the chat-scope-only foreign-bot gate).
 *
 * This module is the pure, I/O-free core: parse the `--bot` specs and build the
 * two messages (a top-level seed = the thread root, and the threaded kickoff
 * that @-mentions the bots with their roles + the brief). The CLI shell
 * (cli.ts) performs the actual sendMessage + replyMessage.
 */
export { resolveSendTarget } from './reply-target.js';
export interface DispatchBot {
    /** open_id as seen by the orchestrator's app (from <available_bots>). */
    openId: string;
    /** Display name, for readable @ rendering / division-of-labor lines. */
    name?: string;
    /** Short role label, e.g. "coder" / "reviewer". */
    role?: string;
}
export type PostNode = {
    tag: 'text';
    text: string;
} | {
    tag: 'at';
    user_id: string;
};
export type PostParagraph = PostNode[];
export interface DispatchMessages {
    /** Plain-text seed (the thread root) — the human-visible "this sub-project exists" header. */
    seedText: string;
    /** Lark 'post' content (paragraphs of nodes) for the threaded kickoff. */
    threadContent: PostParagraph[];
    /** open_ids @-mentioned in the kickoff — the bots that will be triggered. */
    mentionedOpenIds: string[];
}
/**
 * Parse a `--bot` spec `openId[:name[:role]]` into a {@link DispatchBot}.
 * Mirrors the `--mention "open_id:Display Name"` convention, with an optional
 * trailing role segment.
 */
export declare function parseDispatchBotSpec(raw: string): DispatchBot;
/**
 * Build the seed + threaded-kickoff messages for one sub-project dispatch.
 * Throws when there is no title or no bot to dispatch to.
 */
export declare function buildDispatchMessages(input: {
    title: string;
    brief: string;
    bots: DispatchBot[];
}): DispatchMessages;
/**
 * Build the "repo prime" message: a `/repo <path>` command @-mentioning the
 * target bots, sent as a **plain text message** — exactly like a human typing
 * "@bot /repo <path>". Sent as the first message into a freshly-seeded thread,
 * it makes each sub-bot's daemon resolve the working dir and spawn its CLI
 * **idle** (no repo-selection card, no manual "直接开始" click) — i.e. standby.
 *
 * Why text (not a structured `post`): the receiving daemon parses a text
 * message's @ via `resolveMentions` (the same clean path a human @ goes
 * through), whereas a `post`'s at/text nodes go through `renderPostNode`, which
 * drops the `/repo` argument in the live event — see the dispatch debugging
 * notes. `/repo` is an existing botmux command, so this needs no receiving-side
 * change. The `<at>` tags come first so that, once the receiving daemon strips
 * leading mentions, it sees `/repo <path>` as the command.
 */
export declare function buildRepoPrimeText(input: {
    path: string;
    bots: DispatchBot[];
}): {
    text: string;
    mentionedOpenIds: string[];
};
/**
 * Build the report-back message a dispatched sub-bot sends to its orchestrator.
 *
 * In 多话题协作模式 a sub-bot must NOT @ the orchestrator in its own sub-topic —
 * that thread has no orchestrator session, so the orchestrator's daemon would
 * spawn a fresh, context-less one. Instead `botmux report` sends this content
 * **into the orchestrator's own thread** (recorded by `botmux dispatch`),
 * @-mentioning the orchestrator so its existing, context-rich session is the one
 * that wakes up. This is the pure content builder; cli.ts resolves the coords
 * and performs the reply.
 *
 * The @ stays on the first line so the mention renders next to the headline;
 * any further lines become their own paragraphs (Lark 'post' shape).
 */
export declare function buildReportContent(input: {
    orchOpenId: string;
    content: string;
}): PostParagraph[];
/**
 * Footgun guard for the orchestrator→sub-bot direction. A dispatched sub-bot's
 * session lives **inside its sub-topic**, so @-mentioning it from the main chat
 * (e.g. `botmux send --mention <sub-bot>`) doesn't reach that session — it
 * spawns a fresh, context-less one in the chat (the mirror of the report-back
 * problem). To talk to a sub-bot the orchestrator must send INTO its sub-topic
 * (`botmux dispatch --into <seed> --bot <sub-bot>`).
 *
 * Given the dispatch registry (seed → {orchChatId, bots}) and the set of seeds
 * whose sub-topic is still active, return the sub-topic seed to redirect to when
 * `mentionOpenId` is a sub-bot dispatched into an active topic of `chatId`;
 * otherwise null. Only fires for live topics so stale entries don't block sends.
 */
export declare function findSubBotTopic(input: {
    mentionOpenId: string;
    chatId: string;
    registry: Record<string, {
        orchChatId?: string;
        bots?: string[];
    }>;
    activeSeeds: Set<string>;
}): string | null;
/**
 * Resolve where a `botmux report` should go + who to @, so report-back works
 * even when the orchestrator is on a DIFFERENT machine.
 *
 * Same-machine: the dispatch registry (orchestrate-dispatch.json) is local, so
 * `registryEntry` carries the orchestrator's exact coords (incl. orchRoot for a
 * thread-scope orchestrator). Cross-machine: the foreign sub-bot's daemon never
 * wrote that registry, so `registryEntry` is undefined — but everything needed
 * for the common case is on the sub-bot's OWN session: the report goes top-level
 * into the chat the sub-topic lives in (= the orchestrator's chat) and @-s the
 * orchestrator (creatorOpenId, captured from the dispatch @). So we fall back to
 * `{ orchChatId: sessionChatId, orchScope: 'chat', orchRoot: '' }`.
 *
 * orchOpenId prefers `creatorOpenId` (stable, set on every session-creation path
 * incl. foreign-bot auto-create), then `ownerOpenId`, then the drifting
 * `quoteTargetSenderOpenId` as a last resort.
 */
export declare function resolveReportTarget(input: {
    registryEntry?: {
        orchChatId?: string;
        orchScope?: string;
        orchRoot?: string;
    };
    sessionChatId?: string;
    creatorOpenId?: string;
    ownerOpenId?: string;
    quoteTargetSenderOpenId?: string;
}): {
    orchChatId?: string;
    orchScope: string;
    orchRoot: string;
    orchOpenId?: string;
};
/**
 * The footgun check shared by `botmux send`'s explicit-mention guard AND its
 * prose `@Name` auto-injection: returns the sub-topic seed if `mentionOpenId` is
 * a dispatched sub-bot in an active topic that is NOT reachable in the current
 * conversation (so @-ing it here would spawn a context-less session), else null.
 *
 * The bot I'm replying to (`quoteTargetSenderOpenId`) is reachable right here, so
 * it's never treated as off-topic — that's the boundary that stops the guard from
 * blocking a normal reply to a bot conversing with me. Callers block (explicit
 * --mention) or drop (prose injection) on a non-null result, and skip the whole
 * check under `--anyway`.
 */
export declare function offTopicSubBotTopic(input: {
    mentionOpenId: string;
    quoteTargetSenderOpenId?: string;
    chatId: string;
    registry: Record<string, {
        orchChatId?: string;
        bots?: string[];
    }>;
    activeSeeds: Set<string>;
}): string | null;
/**
 * Decide which names of a candidate bot are eligible for prose `@Name`
 * auto-mention injection in `botmux send`.
 *
 * The fan-out bug: a bot writes "@Codex review" in its message; the injector
 * matches each bot by **botName OR cliId**, and the cliId ("codex") is a shared
 * *type* alias — so "@Codex" matches every codex-type bot (Codex分身, Codex二号分身,
 * ttadk(codex), aiden x codex…) and pulls them ALL into the topic, each spawning
 * a session and replying.
 *
 * Fix: the unique `botName` is always eligible (so first-time @-invites still
 * work), but the type-generic `cliId` alias is eligible **only when this bot is
 * actually in the current conversation** (`convoBotAppIds` = bots with an active
 * session in this thread / chat). So "@Codex" resolves to the one codex bot
 * collaborating here, not every same-type bot. `selfAliases` (the sender's own
 * name/cliId) are always excluded.
 */
export declare function eligibleAutoMentionAliases(input: {
    botName?: string;
    cliId?: string;
    larkAppId?: string;
    selfAliases: Set<string>;
    convoBotAppIds: Set<string>;
}): string[];
//# sourceMappingURL=dispatch.d.ts.map