mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 23:54:45 +00:00
docs: document presentation API surface
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
df6c2799805dc3c57924dbb1632d11e7ed08ef4d7759f535998b170f1a10a638 plugin-sdk-api-baseline.json
|
||||
e3526669b79e5eaa3b92e03bece552402209d3cf5b35343c33b62299f71b2efc plugin-sdk-api-baseline.jsonl
|
||||
c8edc84c93c2077d8f37fd9c3626cfa5ea65d0d65f78899427e8251dfe9eac2e plugin-sdk-api-baseline.json
|
||||
d5e2e3ebc8fc54a311f1da43c326bd5b883bbb5d99c2fec117955c887fcc3bac plugin-sdk-api-baseline.jsonl
|
||||
|
||||
@@ -63,6 +63,7 @@ function readSlackOpenClawBlockIndex(blockId: string, prefix: string): number |
|
||||
return Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
||||
}
|
||||
|
||||
/** Resolve existing OpenClaw Block Kit indexes so appended controls keep stable unique IDs. */
|
||||
export function resolveSlackInteractiveBlockOffsets(
|
||||
blocks?: readonly SlackBlock[],
|
||||
): SlackInteractiveBlockRenderOptions {
|
||||
@@ -189,6 +190,7 @@ export function buildSlackInteractiveBlocks(
|
||||
}).blocks;
|
||||
}
|
||||
|
||||
/** Render portable presentation blocks as Slack Block Kit blocks. */
|
||||
export function buildSlackPresentationBlocks(
|
||||
presentation?: MessagePresentation,
|
||||
options: SlackInteractiveBlockRenderOptions = {},
|
||||
|
||||
@@ -99,6 +99,7 @@ export function buildTelegramInteractiveButtons(
|
||||
return rows.length > 0 ? rows : undefined;
|
||||
}
|
||||
|
||||
/** Convert portable presentation controls to Telegram inline keyboard rows. */
|
||||
export function buildTelegramPresentationButtons(
|
||||
presentation?: MessagePresentation,
|
||||
): TelegramInlineButtons | undefined {
|
||||
@@ -122,6 +123,7 @@ export function buildTelegramPresentationButtons(
|
||||
return rows.length > 0 ? rows : undefined;
|
||||
}
|
||||
|
||||
/** Resolve Telegram inline buttons, preserving explicit and legacy button precedence. */
|
||||
export function resolveTelegramInlineButtons(params: {
|
||||
buttons?: TelegramInlineButtons;
|
||||
presentation?: unknown;
|
||||
|
||||
@@ -82,15 +82,23 @@ export type AgentRuntimeProviderHandle = {
|
||||
|
||||
export type AgentRuntimeInteractiveButtonStyle = "primary" | "secondary" | "success" | "danger";
|
||||
|
||||
/** Portable action control exposed to agent runtime reply payloads. */
|
||||
export type AgentRuntimeMessagePresentationButton = {
|
||||
/** User-visible button label. */
|
||||
label: string;
|
||||
/** Callback command or opaque value sent when pressed. */
|
||||
value?: string;
|
||||
/** External URL opened by the button. */
|
||||
url?: string;
|
||||
/** Optional visual style hint for renderers that support styled actions. */
|
||||
style?: AgentRuntimeInteractiveButtonStyle;
|
||||
};
|
||||
|
||||
/** Portable select/menu option exposed to agent runtime reply payloads. */
|
||||
export type AgentRuntimeMessagePresentationOption = {
|
||||
/** User-visible option label. */
|
||||
label: string;
|
||||
/** Callback command or opaque value sent when selected. */
|
||||
value: string;
|
||||
};
|
||||
|
||||
@@ -159,8 +167,11 @@ export type AgentRuntimeMessagePresentationBlock =
|
||||
};
|
||||
|
||||
export type AgentRuntimeMessagePresentation = {
|
||||
/** Optional short heading rendered before blocks when supported. */
|
||||
title?: string;
|
||||
/** Optional severity/status tone for renderers that support toned presentations. */
|
||||
tone?: AgentRuntimeMessagePresentationTone;
|
||||
/** Ordered portable blocks rendered or downgraded by channel adapters. */
|
||||
blocks: AgentRuntimeMessagePresentationBlock[];
|
||||
};
|
||||
|
||||
|
||||
@@ -42,31 +42,52 @@ export type ChannelOutboundPayloadContext = ChannelOutboundContext & {
|
||||
};
|
||||
|
||||
export type ChannelPresentationCapabilities = {
|
||||
/** Whether the channel accepts structured presentation payloads at all. */
|
||||
supported?: boolean;
|
||||
/** Whether the channel can render button action blocks natively. */
|
||||
buttons?: boolean;
|
||||
/** Whether the channel can render select/menu blocks natively. */
|
||||
selects?: boolean;
|
||||
/** Whether the channel can render low-emphasis context blocks natively. */
|
||||
context?: boolean;
|
||||
/** Whether the channel can render divider blocks natively. */
|
||||
divider?: boolean;
|
||||
/** Per-channel limits used to adapt portable presentation blocks before rendering. */
|
||||
limits?: {
|
||||
actions?: {
|
||||
/** Maximum total button/select actions in one message. */
|
||||
maxActions?: number;
|
||||
/** Maximum buttons per rendered action row. */
|
||||
maxActionsPerRow?: number;
|
||||
/** Maximum action rows in one message. */
|
||||
maxRows?: number;
|
||||
/** Maximum user-visible button label length. */
|
||||
maxLabelLength?: number;
|
||||
/** Maximum callback/action value size in UTF-8 bytes. */
|
||||
maxValueBytes?: number;
|
||||
/** Whether action styles such as primary or danger are preserved. */
|
||||
supportsStyles?: boolean;
|
||||
/** Whether disabled button state is preserved. */
|
||||
supportsDisabled?: boolean;
|
||||
/** Whether priority/layout hints affect native rendering. */
|
||||
supportsLayoutHints?: boolean;
|
||||
};
|
||||
selects?: {
|
||||
/** Maximum options in one select/menu block. */
|
||||
maxOptions?: number;
|
||||
/** Maximum user-visible option label length. */
|
||||
maxLabelLength?: number;
|
||||
/** Maximum option callback value size in UTF-8 bytes. */
|
||||
maxValueBytes?: number;
|
||||
};
|
||||
text?: {
|
||||
/** Maximum text length for title, text, and context blocks. */
|
||||
maxLength?: number;
|
||||
/** Unit used by maxLength. Defaults to Unicode code points. */
|
||||
encoding?: "characters" | "utf8-bytes" | "utf16-units";
|
||||
/** Markdown dialect understood by rendered text blocks. */
|
||||
markdownDialect?: "plain" | "markdown" | "html" | "slack-mrkdwn" | "discord-markdown";
|
||||
/** Whether the channel can edit presentation text in-place. */
|
||||
supportsEdit?: boolean;
|
||||
};
|
||||
};
|
||||
@@ -157,8 +178,10 @@ export type ChannelOutboundAdapter = {
|
||||
payload: ReplyPayload;
|
||||
results: readonly OutboundDeliveryResult[];
|
||||
}) => Promise<void> | void;
|
||||
/** Channel-advertised presentation features and limits used by core adaptation. */
|
||||
presentationCapabilities?: ChannelPresentationCapabilities;
|
||||
deliveryCapabilities?: ChannelDeliveryCapabilities;
|
||||
/** Render an adapted portable presentation into channel-native payload data. */
|
||||
renderPresentation?: (params: {
|
||||
payload: ReplyPayload;
|
||||
presentation: MessagePresentation;
|
||||
|
||||
@@ -434,6 +434,12 @@ function adaptTextBlock(
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt a portable presentation to the target channel's advertised capabilities.
|
||||
*
|
||||
* Unsupported controls are downgraded to text/context fallback blocks where possible, and
|
||||
* labels, values, rows, options, styles, disabled state, and text are clipped to channel limits.
|
||||
*/
|
||||
export function adaptMessagePresentationForChannel(params: {
|
||||
presentation: MessagePresentation;
|
||||
capabilities?: ChannelPresentationCapabilities;
|
||||
@@ -508,6 +514,7 @@ export function adaptMessagePresentationForChannel(params: {
|
||||
};
|
||||
}
|
||||
|
||||
/** Return the subset of buttons that can still be rendered under action limits. */
|
||||
export function applyPresentationActionLimits(
|
||||
buttons: readonly MessagePresentationButton[],
|
||||
capabilities?: ChannelPresentationCapabilities,
|
||||
@@ -522,6 +529,7 @@ export function applyPresentationActionLimits(
|
||||
return block.flatMap((entry) => (entry.type === "buttons" ? entry.buttons : []));
|
||||
}
|
||||
|
||||
/** Resolve an action page size that leaves room for reserved actions on the target channel. */
|
||||
export function presentationPageSize(
|
||||
capabilities?: ChannelPresentationCapabilities,
|
||||
reservedActions = 0,
|
||||
|
||||
@@ -177,6 +177,7 @@ function buildApprovalPresentationButtons(
|
||||
}));
|
||||
}
|
||||
|
||||
/** Build the portable approval button presentation for already-resolved actions. */
|
||||
export function buildApprovalPresentationFromActionDescriptors(
|
||||
actions: readonly ExecApprovalActionDescriptor[],
|
||||
): MessagePresentation | undefined {
|
||||
@@ -184,6 +185,7 @@ export function buildApprovalPresentationFromActionDescriptors(
|
||||
return buttons.length > 0 ? { blocks: [{ type: "buttons", buttons }] } : undefined;
|
||||
}
|
||||
|
||||
/** Build the portable approval presentation for an approval id and decision allowlist. */
|
||||
export function buildApprovalPresentation(params: {
|
||||
approvalId: string;
|
||||
ask?: string | null;
|
||||
@@ -198,6 +200,7 @@ export function buildApprovalPresentation(params: {
|
||||
);
|
||||
}
|
||||
|
||||
/** Build the portable exec-approval presentation for command callback buttons. */
|
||||
export function buildExecApprovalPresentation(params: {
|
||||
approvalCommandId: string;
|
||||
ask?: string | null;
|
||||
|
||||
@@ -5,14 +5,21 @@ import {
|
||||
|
||||
export type InteractiveButtonStyle = "primary" | "secondary" | "success" | "danger";
|
||||
|
||||
/** Visual tone for a portable message presentation. */
|
||||
export type MessagePresentationTone = "info" | "success" | "warning" | "danger" | "neutral";
|
||||
|
||||
/** Button style hint for renderers that support styled actions. */
|
||||
export type MessagePresentationButtonStyle = InteractiveButtonStyle;
|
||||
|
||||
/** Portable action control rendered as a button or link by channel adapters. */
|
||||
export type MessagePresentationButton = {
|
||||
/** User-visible button label. */
|
||||
label: string;
|
||||
/** Callback command or opaque value sent when the button is pressed. */
|
||||
value?: string;
|
||||
/** External URL opened by the button instead of sending a callback value. */
|
||||
url?: string;
|
||||
/** Telegram-style web app launch target. */
|
||||
webApp?: {
|
||||
url: string;
|
||||
};
|
||||
@@ -22,13 +29,19 @@ export type MessagePresentationButton = {
|
||||
web_app?: {
|
||||
url: string;
|
||||
};
|
||||
/** Higher-priority buttons are kept first when channel limits require truncation. */
|
||||
priority?: number;
|
||||
/** Disable the button when the target channel supports disabled controls. */
|
||||
disabled?: boolean;
|
||||
/** Optional visual style hint; unsupported channels ignore or normalize it. */
|
||||
style?: InteractiveButtonStyle;
|
||||
};
|
||||
|
||||
/** Portable select/menu option. */
|
||||
export type MessagePresentationOption = {
|
||||
/** User-visible option label. */
|
||||
label: string;
|
||||
/** Callback command or opaque value sent when the option is selected. */
|
||||
value: string;
|
||||
};
|
||||
|
||||
@@ -84,11 +97,13 @@ export type InteractiveReply = {
|
||||
|
||||
export type MessagePresentationTextBlock = {
|
||||
type: "text";
|
||||
/** Primary markdown-ish text rendered in the message body. */
|
||||
text: string;
|
||||
};
|
||||
|
||||
export type MessagePresentationContextBlock = {
|
||||
type: "context";
|
||||
/** Lower-emphasis contextual text, or normal text on channels without context support. */
|
||||
text: string;
|
||||
};
|
||||
|
||||
@@ -98,12 +113,15 @@ export type MessagePresentationDividerBlock = {
|
||||
|
||||
export type MessagePresentationButtonsBlock = {
|
||||
type: "buttons";
|
||||
/** Button row candidates; core may split or truncate them for channel limits. */
|
||||
buttons: MessagePresentationButton[];
|
||||
};
|
||||
|
||||
export type MessagePresentationSelectBlock = {
|
||||
type: "select";
|
||||
/** Optional prompt shown above or inside the select control. */
|
||||
placeholder?: string;
|
||||
/** Menu options; core may truncate them for channel limits. */
|
||||
options: MessagePresentationOption[];
|
||||
};
|
||||
|
||||
@@ -119,8 +137,11 @@ export type MessagePresentationBlock =
|
||||
| MessagePresentationSelectBlock;
|
||||
|
||||
export type MessagePresentation = {
|
||||
/** Optional short heading rendered before blocks when the channel supports it. */
|
||||
title?: string;
|
||||
/** Optional severity/status tone for renderers that support toned presentations. */
|
||||
tone?: MessagePresentationTone;
|
||||
/** Ordered portable blocks rendered or downgraded by the target channel adapter. */
|
||||
blocks: MessagePresentationBlock[];
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import type { ReplyPayload } from "./reply-payload.js";
|
||||
|
||||
const DEFAULT_ALLOWED_DECISIONS = ["allow-once", "allow-always", "deny"] as const;
|
||||
|
||||
/** Build a pending approval reply payload using the portable presentation API. */
|
||||
export function buildApprovalPendingReplyPayload(params: {
|
||||
approvalKind?: "exec" | "plugin";
|
||||
approvalId: string;
|
||||
@@ -46,6 +47,7 @@ export function buildApprovalPendingReplyPayload(params: {
|
||||
};
|
||||
}
|
||||
|
||||
/** Build a resolved approval reply payload with approval metadata but no controls. */
|
||||
export function buildApprovalResolvedReplyPayload(params: {
|
||||
approvalId: string;
|
||||
approvalSlug: string;
|
||||
|
||||
Reference in New Issue
Block a user