Add generic message embeds and actions to Studio#428
Open
NichUK wants to merge 9 commits into
Open
Conversation
There was a problem hiding this comment.
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.contentfields on the backend (not justtext/files). - Extend Studio message/event types + adapters/stores to carry
schema,embeds,actions, andrawContent. - 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 likeschema,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 underpayload.contentwhile ensuringtextis 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.
|
|
||
| export interface MessageActionInput { | ||
| name: string; | ||
| type: 'text' | 'textarea' | 'number' | 'boolean' | 'select' | string; |
Comment on lines
+33
to
+35
| type: 'submit' | 'link' | string; | ||
| label: string; | ||
| style?: 'primary' | 'secondary' | 'danger' | string; |
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 on lines
+2267
to
+2268
| const contentObject = | ||
| typeof messageData.content === "object" ? messageData.content : undefined; |
Comment on lines
+2380
to
+2381
| const contentObject = | ||
| typeof content === "object" ? content : undefined; |
Comment on lines
+2448
to
+2449
| const contentObject = | ||
| typeof messageData.content === "object" ? messageData.content : undefined; |
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", | ||
| }, | ||
| }) |
|
@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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
contentkeys in the workspace messaging backend instead of reducing history totext/files.schema,embeds, andactionsfields to Studio message types and live message ingestion paths.action_responsechannel messages.Refs #427
Verification
PYTHONPATH=C:\Dev\openagents-org-openagents\sdk\src python -m pytest tests\mods\test_messaging_content_embeds.py -q-> passednpm run buildinsdk/studio-> passednpm run typecheckinsdk/studio-> blocked by existingTS2688: Cannot find type definition file for 'diff'before this changeNotes
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
textfallback.