/** * Channel-side contract used by {@link WorkflowProgressBroker} to deliver * workflow progress to a specific messaging surface (Telegram, Feishu, …). * * Why a capability instead of a hook on `ChannelPlugin`? * - Optional opt-in: channels register themselves at boot; no plugin * changes for surfaces that don't ship progress (e.g. SMS). * - Per-channel sane defaults baked in (throttle, mode) — the broker * reads these and lets `channels..workflowProgress` in config * override per deployment. * - Decouples the broker from the {@link ChannelPlugin} interface so * adding new channels never grows broker imports. */ export type WorkflowProgressMode = /** Edit a single message in place (Telegram, Feishu). */ 'edit' /** Append a new message on each key event (WeChat — can't edit). */ | 'append' /** No mid-run updates; final-only message at completion. */ | 'final-only'; export interface WorkflowProgressPostInput { /** Routing key for the run — channel uses it to resolve the destination chat. */ sessionKey: string; /** Pre-rendered progress text (already includes header / phases / logs). */ text: string; /** * Message id returned by the previous `postProgress` for the same run, when * editing in place. `undefined` for the first message of a run, or always * for `append` / `final-only` modes. */ previousMessageId?: string; /** * `true` for the final completion message (sent right after `tool_end`). * Useful for channels that want to bypass their own rate limiting for the * last update (so users always see the conclusion). */ isFinal: boolean; /** * The effective rendering mode for this dispatch — broker resolves it from * the capability's `defaultMode` plus any `channels..workflowProgress.mode` * override. Capabilities can use this to adjust message shape (e.g. WeChat * prefixes mid-run `append` messages with "▾ progress" so the user can tell * them apart from the final summary). * * Optional only to keep hand-rolled callers and unit-test stubs lean — the * broker always provides it. Capabilities that consume the field should * default to `'edit'` when missing. */ mode?: WorkflowProgressMode; } export interface WorkflowProgressPostResult { /** New / unchanged message id to use as `previousMessageId` next time. */ messageId: string; } export interface ChannelProgressCapability { /** Stable channel id matching `channels.` in config (e.g. `telegram`). */ readonly channelId: string; /** True when the underlying platform exposes editMessage semantics. */ readonly supportsEdit: boolean; /** Sane default throttle (ms) for this surface; respects platform limits. */ readonly defaultThrottleMs: number; /** Sane default mode for this surface. */ readonly defaultMode: WorkflowProgressMode; /** * Deliver one progress update. * * Capability is responsible for: * - Resolving `sessionKey` → platform chat/thread id (channel-specific). * - Encoding the text payload (markdown / plain / HTML, line breaks). * - Calling editMessageText vs sendMessage based on `previousMessageId`. * - Honouring platform rate limits (429 retry-after, etc.). * * Throw on hard failure; the broker logs and drops the update. */ postProgress(input: WorkflowProgressPostInput): Promise; }