import type { ManagedHookOrigin } from '../services/hook-runner.js'; export type SendMessageFn = (larkAppId: string, chatId: string, content: string, msgType?: string, uuid?: string, hookContext?: Record, options?: { suppressHook?: boolean; beforeHook?: () => void | Promise; hookOrigin?: ManagedHookOrigin; }) => Promise; export type ReplyMessageFn = (larkAppId: string, messageId: string, content: string, msgType?: string, replyInThread?: boolean, uuid?: string, hookContext?: Record, options?: { suppressHook?: boolean; beforeHook?: () => void | Promise; hookOrigin?: ManagedHookOrigin; }) => Promise; export type DispatchPrimaryDeps = { sendMessage: SendMessageFn; replyMessage: ReplyMessageFn; }; /** First attachment path that aliases stdin, or null if none do. */ export declare function findStdinAliasAttachment(paths: readonly string[]): string | null; export type SlashSendValidation = { ok: true; command: string; } | { ok: false; error: string; }; /** * Validate the body of a `botmux send --slash ""`. * * `--slash` exists so one bot can hand another a NATIVE slash command that the * receiving daemon relays into the CLI verbatim (passthrough: /clear, /model, * …) or routes as a daemon command (/close, …). The ordinary `send` path wraps * every message in an interactive card whose body picks up a `[🔊 语音总结]` * footer line, so the receiver sees a MULTI-LINE message and * `parseSlashCommandInvocation` (which only treats /schedule|/role|/fork as * multi-line commands) drops it to an ordinary prompt — the command never * reaches the passthrough/daemon router. A `--slash` send therefore MUST go out * as a single-line plain-`text` message. * * Fail LOUD rather than silently sending junk: the content has to be exactly one * line and start with `/`. Leading/trailing whitespace is trimmed (a trailing * newline from a heredoc is the common case); an interior newline is rejected so * the caller notices instead of the daemon quietly treating it as prose. */ export declare function validateSlashSend(raw: string): SlashSendValidation; export type SendFileAttachmentsDeps = { uploadFile: (appId: string, path: string) => Promise; dispatch: (content: string, msgType: string) => Promise; beforeEffect?: () => void | Promise; }; export type SendFileAttachmentsResult = { sent: string[]; failed: { path: string; error: string; }[]; }; /** * Upload + post each file as its own message, best-effort. By the time this * runs the primary message has already been delivered, so a failure on one * attachment must NOT throw: letting it bubble would make the caller report * total failure (exit 1) for an already-sent message, which drives resends and * duplicates. Collect failures so the caller can surface them as a warning * while still reporting the primary send as the success it was. */ export declare function sendFileAttachments(deps: SendFileAttachmentsDeps, appId: string, files: readonly string[]): Promise; /** * Decide whether a send is a "pure video" send — one delivered as a standalone * Lark media message with no text/card primary. * * A media message CANNOT embed an ``, so a send that also carries mentions * must NOT be pure-video: it has to go through the card path (which renders the * @ on the footer) and send the video as a follow-up attachment. Otherwise the * mention silently never fires while the success output still reports it. */ export declare function shouldSendAsPureVideo(input: { hasBodyText: boolean; imageCount: number; fileCount: number; videoCount: number; mentionCount: number; }): boolean; export type VideoAttachmentInput = { videoPath: string; coverPath: string; durationMs: number; }; export type VideoAttachmentValidationResult = { ok: true; videos: VideoAttachmentInput[]; } | { ok: false; error: string; }; export declare function validateVideoAttachments(videos: readonly string[], covers: readonly string[]): VideoAttachmentValidationResult; export type NormalizedInteractiveCardResult = { ok: true; card: Record; cardJson: string; } | { ok: false; error: string; }; /** * Normalize user-supplied Lark/Feishu interactive card JSON into the raw card * body expected by the Lark send/reply APIs. Accepts either: * - direct card JSON: {"schema":"2.0", ...} * - webhook/openapi-style wrapper: {"msg_type":"interactive","card":{...}} * - wrapper with string/object content: {"msg_type":"interactive","content":"{...}"} * * Deliberately rejects callback actions. botmux owns a broad card-action * namespace (close/restart/ask/relay/dashboard/etc.); arbitrary callbacks from * a CLI-created card would be routed through those handlers with host-side * privileges after a user clicks. Display cards and open-url buttons still work. */ export declare function normalizeInteractiveCardInput(raw: string): NormalizedInteractiveCardResult; export type SendVideoAttachmentsDeps = { uploadFile: (appId: string, path: string) => Promise; uploadImage: (appId: string, path: string) => Promise; dispatch: (content: string, msgType: string) => Promise; primaryDispatch?: (content: string, msgType: string) => Promise; /** Optional hard cap checked before any upload/dispatch. Managed VC pure-video * replies set this to one because only the primary media message has a durable * action/provider identity; later bare media sends would duplicate on replay. */ maxMessages?: number; beforeEffect?: () => void | Promise; }; export type SendVideoAttachmentsResult = { sent: string[]; failed: { path: string; coverPath: string; error: string; }[]; }; export declare function sendVideoAttachments(deps: SendVideoAttachmentsDeps, appId: string, videos: readonly VideoAttachmentInput[]): Promise; export type DispatchPrimaryOptions = { appId: string; targetChatId: string; quoteTargetId: string | null | undefined; content: string; msgType: string; hookContext: Record; /** Stable provider idempotency key for a crash-replayed primary effect. */ uuid?: string; MessageWithdrawnError: new (...args: any[]) => Error; dispatch: (content: string, msgType: string, uuid?: string, suppressHook?: boolean) => Promise; /** Provider UUID reconciliation must not repeat the local outbound hook. */ suppressHook?: boolean; /** Revalidate immediately before the distinct post-provider hook effect. */ beforeHook?: () => void | Promise; hookOrigin?: ManagedHookOrigin; /** Revalidate any side-effect authority after an awaited quote failure and * immediately before the fallback creates a top-level message. */ beforeQuoteFallback?: () => void | Promise; /** Revalidate managed authority immediately before each provider call. */ beforeEffect?: () => void | Promise; onQuoteWithdrawn?: (messageId: string) => void; }; export type DispatchPrimaryResult = { messageId: string; primaryQuotedId: string | null; }; export declare function dispatchPrimaryMessage(deps: DispatchPrimaryDeps, opts: DispatchPrimaryOptions): Promise; //# sourceMappingURL=send-dispatch.d.ts.map