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; |
| type: 'submit' | 'link' | string; | ||
| label: string; | ||
| style?: 'primary' | 'secondary' | 'danger' | string; |
| 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, |
| 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 | ||
| } |
| const renderMessageActions = (message: UnifiedMessage, actions?: MessageAction[]) => { | ||
| if (!actions || actions.length === 0 || !onMessageAction) return null |
| const contentObject = | ||
| typeof messageData.content === "object" ? messageData.content : undefined; |
| const contentObject = | ||
| typeof content === "object" ? content : undefined; |
| const contentObject = | ||
| typeof messageData.content === "object" ? messageData.content : undefined; |
| const contentObject = | ||
| typeof messageData.content === "object" ? messageData.content : undefined; |
| 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. |
|
Closing this one, with thanks for the work — the embed and action rendering was a nice design. Correction to my earlier wording: the Studio source is still in the tree under sdk/studio, but it is no longer built or shipped. Its build workflow was removed this month, it has had no changes since July, and the Workspace web app has taken over its role, so we are not taking new Studio features and this branch has gone four months without a path to merge. The one backend-side idea here, preserving arbitrary message content keys in the messaging mod rather than reducing history to text/files, is still relevant to the SDK on its own. If you'd like to carry that part forward, a small PR against sdk/src/openagents/mods/workspace/messaging on current develop, with the test, would be welcome. |
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.