feat(plugin-sdk): add typed presentation command actions#88721
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed May 31, 2026, 1:34 PM ET / 17:34 UTC. Summary PR surface: Source +377, Tests +754, Docs +14, Generated 0. Total +1145 across 41 files. Reproducibility: unclear. The review failed before ClawSweeper could establish a reproduction path. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Review detailsBest possible solution: Retry the Codex review after fixing the execution failure. Do we have a high-confidence way to reproduce the issue? Unclear. The review failed before ClawSweeper could establish a reproduction path. Is this the best way to solve the issue? Unclear. Retry the review first so ClawSweeper can evaluate the actual issue and fix direction. AGENTS.md: unclear because the file could not be read completely. Codex review notes: model gpt-5.5, reasoning high; reviewed against 1a65425a6eab. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +377, Tests +754, Docs +14, Generated 0. Total +1145 across 41 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
18836de to
fea05cd
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 18836de8ed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const callbackData = resolveSlackControlValue(button); | ||
| const value = | ||
| button.value && isWithinSlackLimit(button.value, SLACK_BUTTON_VALUE_MAX) | ||
| ? button.value | ||
| callbackData && isWithinSlackLimit(callbackData, SLACK_BUTTON_VALUE_MAX) | ||
| ? callbackData | ||
| : undefined; | ||
| const url = | ||
| button.url && isWithinSlackLimit(button.url, SLACK_BUTTON_URL_MAX) ? button.url : undefined; |
There was a problem hiding this comment.
Preserve fallback for unrenderable Slack command buttons
When a presentation contains any renderable text/title block plus a buttons block whose buttons use action: { type: "command" }, resolveSlackControlValue() returns undefined for non-approval commands, this branch drops the button, and Slack renderPresentation still succeeds because the text block produced Block Kit output. Core then strips the presentation instead of running the text fallback, so Slack users see the surrounding text but not the available action labels; add a fallback block for dropped controls or decline rendering when controls cannot be represented.
Useful? React with 👍 / 👎.
| const callbackDataKind = resolveDiscordSelectCallbackDataKind(block.options); | ||
| if (callbackDataKind === "mixed") { | ||
| continue; |
There was a problem hiding this comment.
Preserve fallback for mixed Discord select actions
When a select mixes command and callback actions and the presentation also has a title or text block, this continue omits the select while buildDiscordPresentationComponents still returns the remaining blocks. The outbound core treats that as a successful render and removes the original presentation, so the select option labels never appear as fallback; this only falls back today when the mixed select is the only block.
Useful? React with 👍 / 👎.
1060bc8 to
8f02083
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8f02083c49
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (button.action?.type === "callback") { | ||
| return undefined; |
There was a problem hiding this comment.
Preserve fallback for unsupported Feishu callback buttons
When a presentation has a renderable Feishu button (for example a command or URL) plus a button.action of type callback, this branch makes the callback button unrenderable, but buildFeishuPresentationCardElements() still returns the other card elements. The rendered card is then sent as native channelData.feishu.card and the original presentation is stripped, so users see the command/URL button but never see the callback button label as fallback; only the all-callback case falls back today. Add a fallback element for omitted callback-action buttons or decline rendering the buttons block when any action is unsupported.
Useful? React with 👍 / 👎.
8f02083 to
e116ae7
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e116ae7589
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| action: Type.Optional(presentationActionSchema), | ||
| value: Type.Optional(Type.String()), |
There was a problem hiding this comment.
Require select options to include an action or value
When the message tool schema is shown to the agent, a select option with only label now validates because both action and value are optional. That payload is later normalized by normalizeOption(), which drops options where neither record.value nor an action-derived value exists, so an agent following this schema can produce a select whose options silently disappear instead of being rejected or corrected. Make the option schema require at least one of action or value now that value is no longer mandatory.
Useful? React with 👍 / 👎.
e116ae7 to
f8c700f
Compare
|
Verification before merge: Behavior addressed: core now carries typed message presentation actions for native slash commands and callbacks; channel plugins map the actions they can preserve.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f8c700f4a7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const callbackData = resolveSlackControlValue(button); | ||
| const value = | ||
| button.value && isWithinSlackLimit(button.value, SLACK_BUTTON_VALUE_MAX) | ||
| ? button.value | ||
| callbackData && isWithinSlackLimit(callbackData, SLACK_BUTTON_VALUE_MAX) | ||
| ? callbackData | ||
| : undefined; |
There was a problem hiding this comment.
Dispatch Slack callback actions to plugin handlers
When a presentation button uses action: { type: "callback" }, this now puts the opaque callback value into a Slack openclaw_reply_buttons_* action. I checked extensions/slack/src/monitor/events/interactions.block-actions.ts:929-945: reply action ids only run exec/plugin-binding approval handling and skip dispatchSlackPluginInteraction, so plugin callback actions from Slack presentations are never delivered to the plugin and instead fall through as a generic system event. This breaks the new documented callback-action contract for Slack buttons/selects that are meant to carry plugin data.
Useful? React with 👍 / 👎.
Summary
MessagePresentationActioncommand/callback controls to core payload normalization, the message tool schema, runtime plan types, and plugin SDK exports.Real behavior proof
Behavior addressed: Core owns native slash command presentation actions; message channel plugins map typed command/callback actions only when they can preserve semantics.
Real environment tested: Local source checkout on macOS, Node/pnpm repo toolchain; prior delegated Testbox
pnpm check:changedalso run.Exact steps or command run after this patch:
node scripts/run-vitest.mjs src/interactive/payload.test.ts src/channels/plugins/outbound/interactive.test.ts src/agents/tools/message-tool.test.ts src/auto-reply/reply/commands-diagnostics.test.ts extensions/codex/src/command-plugins-management.test.ts extensions/codex/src/commands.test.ts extensions/telegram/src/button-types.test.ts extensions/telegram/src/bot.test.ts extensions/telegram/src/bot.create-telegram-bot.test.ts extensions/discord/src/shared-interactive.test.ts extensions/discord/src/monitor/monitor.test.ts extensions/slack/src/shared-interactive.test.ts extensions/mattermost/src/channel.test.ts extensions/msteams/src/welcome-card.test.ts extensions/feishu/src/channel.test.ts extensions/feishu/src/outbound.test.ts;pnpm tsgo:core;pnpm tsgo:extensions;pnpm tsgo:extensions:test;pnpm lint:core;pnpm lint:extensions;pnpm docs:list;pnpm docs:check-mdx;pnpm plugin-sdk:api:gen;pnpm plugin-sdk:api:check;/Users/steipete/Projects/agent-skills/skills/autoreview/scripts/autoreview --mode local;pnpm build;git diff --check.Evidence after fix: Focused representative Vitest pass completed 10 shards / 649 tests; autoreview returned clean with no accepted/actionable findings; build completed successfully.
Observed result after fix: Typed command actions survive through core and capable channels; opaque callbacks are not reinterpreted as native commands/approvals; unsupported channel controls fall back to visible text instead of dispatching the wrong action.
What was not tested: No live Telegram/Discord/Slack/Mattermost/Feishu/Microsoft Teams workspace click-through was run in this branch.
Verification
node scripts/run-vitest.mjs src/interactive/payload.test.ts src/channels/plugins/outbound/interactive.test.ts src/agents/tools/message-tool.test.ts src/auto-reply/reply/commands-diagnostics.test.ts extensions/codex/src/command-plugins-management.test.ts extensions/codex/src/commands.test.ts extensions/telegram/src/button-types.test.ts extensions/telegram/src/bot.test.ts extensions/telegram/src/bot.create-telegram-bot.test.ts extensions/discord/src/shared-interactive.test.ts extensions/discord/src/monitor/monitor.test.ts extensions/slack/src/shared-interactive.test.ts extensions/mattermost/src/channel.test.ts extensions/msteams/src/welcome-card.test.ts extensions/feishu/src/channel.test.ts extensions/feishu/src/outbound.test.tspnpm tsgo:corepnpm tsgo:extensionspnpm tsgo:extensions:testpnpm lint:corepnpm lint:extensionspnpm docs:listpnpm docs:check-mdxpnpm plugin-sdk:api:genpnpm plugin-sdk:api:check/Users/steipete/Projects/agent-skills/skills/autoreview/scripts/autoreview --mode localpnpm buildgit diff --checkKnown proof gap:
pnpm check:changedvia delegated Testbox failed on an unrelated current-main type fixture:src/agents/acp-spawn.test.ts(984,15): 'thinking' does not exist.... Testbox id:tbx_01ksz72h2jk9hm9tx9jggqxyxz; run: https://github.com/openclaw/openclaw/actions/runs/26715332777.