惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
The Cloudflare Blog
博客园_首页
Jina AI
Jina AI
WordPress大学
WordPress大学
小众软件
小众软件
阮一峰的网络日志
阮一峰的网络日志
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 三生石上(FineUI控件)
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 叶小钗
美团技术团队
IT之家
IT之家
爱范儿
爱范儿
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
量子位
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
博客园 - 【当耐特】

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
feat: add presentation capability limits · openclaw/openc...
steipete · 2026-05-17 · via Recent Commits to openclaw:main

@@ -57,7 +57,10 @@ type MessagePresentationButton = {

5757

value?: string;

5858

url?: string;

5959

webApp?: { url: string };

60+

/** @deprecated Use webApp. Accepted for legacy JSON payloads only. */

6061

web_app?: { url: string };

62+

priority?: number;

63+

disabled?: boolean;

6164

style?: "primary" | "secondary" | "success" | "danger";

6265

};

6366

@@ -82,11 +85,19 @@ Button semantics:

8285

- `value` is an application action value routed back through the channel's

8386

existing interaction path when the channel supports clickable controls.

8487

- `url` is a link button. It can exist without `value`.

85-

- `webApp` and `web_app` describe a channel-native web app button. Telegram

86-

renders this as `web_app` and only supports it in private chats.

88+

- `webApp` describes a channel-native web app button. Telegram renders this

89+

as `web_app` and only supports it in private chats. `web_app` is still

90+

accepted in loose JSON payloads for compatibility, but TypeScript producers

91+

should use `webApp`.

8792

- `label` is required and is also used in text fallback.

8893

- `style` is advisory. Renderers should map unsupported styles to a safe

8994

default, not fail the send.

95+

- `priority` is optional. When a channel advertises action limits and controls

96+

must be dropped, core keeps higher-priority buttons first and preserves

97+

original order among equal priority buttons. When all controls fit, authored

98+

order is preserved.

99+

- `disabled` is optional. Channels must opt in with `supportsDisabled`; otherwise

100+

core degrades the disabled control to non-interactive fallback text.

9010191102

Select semantics:

92103

@@ -205,6 +216,27 @@ const adapter: ChannelOutboundAdapter = {

205216

selects: true,

206217

context: true,

207218

divider: true,

219+

limits: {

220+

actions: {

221+

maxActions: 25,

222+

maxActionsPerRow: 5,

223+

maxRows: 5,

224+

maxLabelLength: 80,

225+

maxValueBytes: 100,

226+

supportsStyles: true,

227+

supportsDisabled: false,

228+

},

229+

selects: {

230+

maxOptions: 25,

231+

maxLabelLength: 100,

232+

maxValueBytes: 100,

233+

},

234+

text: {

235+

maxLength: 2000,

236+

encoding: "characters",

237+

markdownDialect: "discord-markdown",

238+

},

239+

},

208240

},

209241

deliveryCapabilities: {

210242

pin: true,

@@ -218,10 +250,49 @@ const adapter: ChannelOutboundAdapter = {

218250

};

219251

```

220252221-

Capability fields are intentionally simple booleans. They describe what the

222-

renderer can make interactive, not every native platform limit. Renderers still

223-

own platform-specific limits such as maximum button count, block count, and

224-

card size.

253+

Capability booleans describe what the renderer can make interactive. Optional

254+

`limits` describe the generic envelope core can adapt before calling the

255+

renderer:

256+257+

```ts

258+

type ChannelPresentationCapabilities = {

259+

supported?: boolean;

260+

buttons?: boolean;

261+

selects?: boolean;

262+

context?: boolean;

263+

divider?: boolean;

264+

limits?: {

265+

actions?: {

266+

maxActions?: number;

267+

maxActionsPerRow?: number;

268+

maxRows?: number;

269+

maxLabelLength?: number;

270+

maxValueBytes?: number;

271+

supportsStyles?: boolean;

272+

supportsDisabled?: boolean;

273+

supportsLayoutHints?: boolean;

274+

};

275+

selects?: {

276+

maxOptions?: number;

277+

maxLabelLength?: number;

278+

maxValueBytes?: number;

279+

};

280+

text?: {

281+

maxLength?: number;

282+

encoding?: "characters" | "utf8-bytes" | "utf16-units";

283+

markdownDialect?: "plain" | "markdown" | "html" | "slack-mrkdwn" | "discord-markdown";

284+

supportsEdit?: boolean;

285+

};

286+

};

287+

};

288+

```

289+290+

Core applies generic limits to semantic controls before rendering. Renderers

291+

still own final provider-specific validation and clipping for native block

292+

count, card size, URL limits, and provider quirks that cannot be expressed in

293+

the generic contract. If limits remove every control from a block, core keeps

294+

the labels as non-interactive context text so the delivered message still has a

295+

visible fallback.

225296226297

## Core render flow

227298

@@ -230,10 +301,12 @@ When a `ReplyPayload` or message action includes `presentation`, core:

230301

1. Normalizes the presentation payload.

231302

2. Resolves the target channel's outbound adapter.

232303

3. Reads `presentationCapabilities`.

233-

4. Calls `renderPresentation` when the adapter can render the payload.

234-

5. Falls back to conservative text when the adapter is absent or cannot render.

235-

6. Sends the resulting payload through the normal channel delivery path.

236-

7. Applies delivery metadata such as `delivery.pin` after the first successful

304+

4. Applies generic capability limits such as action count, label length, and

305+

select option count when the adapter advertises them.

306+

5. Calls `renderPresentation` when the adapter can render the payload.

307+

6. Falls back to conservative text when the adapter is absent or cannot render.

308+

7. Sends the resulting payload through the normal channel delivery path.

309+

8. Applies delivery metadata such as `delivery.pin` after the first successful

237310

sent message.

238311239312

Core owns fallback behavior so producers can stay channel-agnostic. Channel

@@ -303,15 +376,20 @@ code:

303376304377

```ts

305378

import {

379+

adaptMessagePresentationForChannel,

380+

applyPresentationActionLimits,

306381

interactiveReplyToPresentation,

307382

normalizeMessagePresentation,

383+

presentationPageSize,

308384

presentationToInteractiveControlsReply,

309385

presentationToInteractiveReply,

310386

renderMessagePresentationFallbackText,

311387

} from "openclaw/plugin-sdk/interactive-runtime";

312388

```

313389314-

New code should accept or produce `MessagePresentation` directly.

390+

New code should accept or produce `MessagePresentation` directly. Existing

391+

`interactive` payloads are a deprecated subset of `presentation`; runtime

392+

support remains for older producers.

315393316394

`presentationToInteractiveReply(...)` preserves visible presentation text by

317395

mapping the title, text, context, buttons, and selects into the older

@@ -351,7 +429,9 @@ messages where the provider supports those operations.

351429

- Implement `renderPresentation` in runtime code, not control-plane plugin

352430

setup code.

353431

- Keep native UI libraries out of hot setup/catalog paths.

354-

- Preserve platform limits in the renderer and tests.

432+

- Declare generic capability limits on `presentationCapabilities.limits` when

433+

they are known.

434+

- Preserve final platform limits in the renderer and tests.

355435

- Add fallback tests for unsupported buttons, selects, URL buttons, title/text

356436

duplication, and mixed `message` plus `presentation` sends.

357437

- Add delivery pin support through `deliveryCapabilities.pin` and