feat(realtime): add unified set() method for atomic prompt + image updates#76
feat(realtime): add unified set() method for atomic prompt + image updates#76AdirAmsalem wants to merge 11 commits intomainfrom
Conversation
Add SetMessage and SetAckMessage types for the new 'set'/'set_ack' wire protocol. Include both in IncomingWebRTCMessage, OutgoingWebRTCMessage, and OutgoingMessage unions. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add set() to the realtime client, allowing atomic prompt + image updates in a single wire message. Omitted fields are unchanged, null clears. - webrtc-connection: add sendSet() with ack/timeout handling - webrtc-manager: pass-through sendSet to connection - methods: add set() with Zod validation (at least one of prompt/image required) - client: extract shared imageToBase64 helper, wire set() into RealTimeClient - index: export SetInput type Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add 20 new unit tests covering: - SetMessage/SetAckMessage type structure validation - sendSet timeout (default and custom) - set() input validation (empty, prompt-only, image-only, both, null-to-clear) - Wire message correctness (omitted fields not present) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Update realtime examples to demonstrate set() as the primary API for prompt and image updates. Keep setPrompt() references for backward compatibility. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
commit: |
| this.websocketMessagesEmitter.on("setAck", listener); | ||
| this.send(message); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Missing ack correlation causes wrong promise resolution on concurrent calls
Medium Severity
sendSet resolves on the first setAck message received, with no correlation to the request that produced it. Unlike the existing setPrompt flow, which matches promptAckMessage.prompt === parsedInput.data.prompt, SetAckMessage has no identifying field, so when multiple set() calls are in-flight (as the examples demonstrate with fire-and-forget in input handlers), the first server ack resolves or rejects all pending promises indiscriminately. A failed request can appear to succeed if an earlier request's success ack arrives first.
Additional Locations (1)
- set() now uses full state replacement: omitted fields are cleared on backend - Wire message only includes provided fields (undefined stripped by JSON) - Remove useless type-construction tests, deduplicate mock setup
| } | ||
| if (image !== undefined && image !== null) { | ||
| message.image_data = await imageToBase64(image); | ||
| } |
There was a problem hiding this comment.
Passing image: null silently fails to clear image
High Severity
The set() method accepts image: null (the Zod schema validates it, and the refine check treats it as "provided"), but the condition image !== undefined && image !== null skips adding any image_data field to the wire message. This makes set({ image: null }) send { type: "set" } — identical to omitting image entirely. The server cannot distinguish "clear image" from "no change." The SetMessage type also lacks null in image_data's type union. Compare with the existing setImage path which correctly sends image_data: null on the wire to clear.
Additional Locations (1)
- Updated comments to specify that set() replaces the full state, while setPrompt() is for prompt-only updates. - Enhanced clarity on the behavior of setPrompt() regarding reference images. - Removed outdated comments to improve code readability.
- Updated SetMessage and SetAckMessage types to SetInputMessage and SetInputAckMessage, respectively, to better reflect their purpose. - Adjusted all references in the codebase, including methods and tests, to use the new naming convention. - Ensured consistency in message types across the WebRTC connection and manager.


Summary
realtimeClient.set({ prompt, enhance, image })— a single method to atomically update prompt and/or reference image in one wire messagesetPrompt()andsetImage()for backward compatibilityset()as the primary APIMotivation
Lucy 2 supports both prompt and reference image. The current API requires separate
setPrompt()andsetImage()calls, which can't be sent atomically.setImage()also duplicatessetPrompt()functionality with its optional prompt parameter, making the API confusing.The new
set()method solves this:Wire Protocol
Commits
SetMessage,SetAckMessageadded to type unionssendSet()on connection/manager,set()with Zod validation in methods, wired into clientset()as primary APITest Results
Note
Medium Risk
Introduces a new realtime wire message (
set_input) and ack/timeout path through the WebRTC connection, so runtime behavior now depends on server support and correct message/ack handling; existing APIs remain for compatibility.Overview
Adds a new
realtimeClient.set()API to atomically update realtime prompt and/or reference image in a single request, with Zod validation and a 30s ack-based timeout.Extends the realtime wire protocol with
set_input/set_input_ack, plumbs it throughWebRTCConnection/WebRTCManager, factors image-to-base64 handling for reuse, and exports the newSetInputtype.Updates realtime examples to prefer
set()and adds unit tests coveringset()validation/wire format plussendSet()timeout behavior.Written by Cursor Bugbot for commit 3546912. This will update automatically on new commits. Configure here.