Skip to content

Add generic message embeds and actions to Studio#428

Open
NichUK wants to merge 9 commits into
openagents-org:developfrom
NichUK:feature/message-embeds-actions
Open

Add generic message embeds and actions to Studio#428
NichUK wants to merge 9 commits into
openagents-org:developfrom
NichUK:feature/message-embeds-actions

Conversation

@NichUK

@NichUK NichUK commented Jun 1, 2026

Copy link
Copy Markdown

Summary

  • Preserve arbitrary message content keys in the workspace messaging backend instead of reducing history to text/files.
  • Add generic schema, embeds, and actions fields to Studio message types and live message ingestion paths.
  • Render generic embed blocks and action buttons in Studio, and post action submissions back as structured action_response channel messages.

Refs #427

Verification

  • PYTHONPATH=C:\Dev\openagents-org-openagents\sdk\src python -m pytest tests\mods\test_messaging_content_embeds.py -q -> passed
  • npm run build in sdk/studio -> passed
  • npm run typecheck in sdk/studio -> blocked by existing TS2688: Cannot find type definition file for 'diff' before this change

Notes

The implementation is intentionally generic rather than approval-specific. Approval buttons, rejection reasons, task claiming, assignment, reruns, request-changes, and release controls can all use the same embed/action contract while older clients continue to rely on the text fallback.

Copilot AI review requested due to automatic review settings June 1, 2026 13:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds support for structured message content (schema/embeds/actions) end-to-end so Studio can render embeds and allow users to trigger message actions.

Changes:

  • Preserve arbitrary payload.content fields on the backend (not just text/files).
  • Extend Studio message/event types + adapters/stores to carry schema, embeds, actions, and rawContent.
  • Render embeds in the UI and add action buttons with a callback that posts an “action response” message.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
tests/mods/test_messaging_content_embeds.py Adds a regression test ensuring _extract_content_from_event preserves embeds/actions/custom fields.
sdk/studio/src/types/message.ts Introduces MessageEmbed/MessageAction types and extends UnifiedMessage + adapter mapping to include structured content.
sdk/studio/src/types/events.ts Extends ThreadMessage.content to optionally include schema/embeds/actions.
sdk/studio/src/stores/chatStore.ts Propagates schema/embeds/actions/rawContent into UnifiedMessage objects created from incoming events.
sdk/studio/src/pages/messaging/components/MessageRenderer.tsx Renders embeds and action buttons; collects action input values and invokes a callback.
sdk/studio/src/pages/messaging/MessagingView.tsx Wires action handling to post an action-response message back to the channel.
sdk/src/openagents/mods/workspace/messaging/mod.py Changes _extract_content_from_event to return a shallow copy of payload.content (preserving arbitrary keys).
Comments suppressed due to low confidence (1)

sdk/src/openagents/mods/workspace/messaging/mod.py:2269

  • The implementation now returns a shallow copy of payload.content (including arbitrary keys like schema, embeds, actions, custom, etc.). The docstring still claims it returns only text and optionally files. Please update the docstring to reflect that it preserves all fields under payload.content while ensuring text is always present.
    def _extract_content_from_event(self, event: Event) -> Dict[str, Any]:
        """Extract full content (text and files) from an Event object's payload.

        This handles the nested content structure: payload.content

        Args:
            event: The Event object to extract content from

        Returns:
            Dict with text and optionally files

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread sdk/studio/src/types/message.ts Outdated

export interface MessageActionInput {
name: string;
type: 'text' | 'textarea' | 'number' | 'boolean' | 'select' | string;
Comment thread sdk/studio/src/types/message.ts Outdated
Comment on lines +33 to +35
type: 'submit' | 'link' | string;
label: string;
style?: 'primary' | 'secondary' | 'danger' | string;
Comment thread sdk/studio/src/types/message.ts Outdated
Comment on lines +172 to +183
const contentObject =
raw.content && typeof raw.content === 'object' ? raw.content as Record<string, any> : undefined;

return {
id: (isDirectMessage ? raw.event_id : raw.message_id) || '',
senderId: (isDirectMessage ? raw.source_id : raw.sender_id) || '',
timestamp: raw.timestamp, // 10 digits vs 13 digits,
content: isDirectMessage ? raw.payload.content.text : content,
schema: contentObject?.schema,
embeds: Array.isArray(contentObject?.embeds) ? contentObject.embeds : undefined,
actions: Array.isArray(contentObject?.actions) ? contentObject.actions : undefined,
rawContent: contentObject,
Comment on lines +216 to +231
const collectActionValues = (action: MessageAction): Record<string, any> | null => {
const values: Record<string, any> = {}
for (const requirement of action.requires || []) {
if (requirement.type === "boolean") {
values[requirement.name] = true
continue
}
const label = requirement.label || requirement.name
const value = window.prompt(label)
if (requirement.required && (!value || !value.trim())) {
return null
}
values[requirement.name] = value || ""
}
return values
}
Comment on lines +287 to +288
const renderMessageActions = (message: UnifiedMessage, actions?: MessageAction[]) => {
if (!actions || actions.length === 0 || !onMessageAction) return null
Comment thread sdk/studio/src/stores/chatStore.ts Outdated
Comment on lines +2267 to +2268
const contentObject =
typeof messageData.content === "object" ? messageData.content : undefined;
Comment thread sdk/studio/src/stores/chatStore.ts Outdated
Comment on lines +2380 to +2381
const contentObject =
typeof content === "object" ? content : undefined;
Comment thread sdk/studio/src/stores/chatStore.ts Outdated
Comment on lines +2448 to +2449
const contentObject =
typeof messageData.content === "object" ? messageData.content : undefined;
Comment thread sdk/studio/src/stores/chatStore.ts Outdated
Comment on lines +2614 to +2615
const contentObject =
typeof messageData.content === "object" ? messageData.content : undefined;
Comment on lines +573 to +591
const response = await connector.sendEvent({
event_name: EventNames.THREAD_CHANNEL_MESSAGE_POST,
source_id: connectionStatus.agentId || agentName,
destination_id: `channel:${currentChannel}`,
payload: {
channel: currentChannel,
content: {
text: [
`Action response: ${action.label}`,
`source_message_id: ${message.id}`,
`action_id: ${action.id}`,
...detailLines,
].join("\n"),
schema: "openagents.message.v1",
action_response: actionResponse,
},
message_type: "channel_message",
},
})
@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown

@NichUK is attempting to deploy a commit to the Raphael's projects Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants