feat(companion): expose relay/tab handles for embedding hosts (OpenHuman wiring) - #24
feat(companion): expose relay/tab handles for embedding hosts (OpenHuman wiring)#24graycyrus wants to merge 2 commits into
Conversation
…abs for embedding hosts
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
| pub fn bind_run( | ||
| &self, | ||
| run_id: impl Into<String>, | ||
| tab_id: TabId, | ||
| ) -> Result<(), CompanionServerError> { | ||
| self.inner | ||
| .relay | ||
| .lock() | ||
| .map_err(|_| lock_error())? | ||
| .tabs_mut() | ||
| .bind_run(run_id.into(), tab_id) | ||
| .map_err(super::RelayError::from)?; | ||
| Ok(()) | ||
| } | ||
|
|
||
| /// Releases a run→tab binding after an external run settles. Idempotent. | ||
| pub fn unbind_run(&self, run_id: &str) { | ||
| if let Ok(mut relay) = self.inner.relay.lock() { | ||
| relay.tabs_mut().unbind_run(run_id); | ||
| } | ||
| } |
There was a problem hiding this comment.
bind_run / cancel_workflow asymmetry for external runs
External runs registered via bind_run are never inserted into self.inner.runs, so cancel_workflow returns false immediately for them. If an OpenHuman workflow is running a browser graph and the host wants to abort mid-flight (e.g., on user request), calling cancel_workflow will silently do nothing — neither the CancellationToken signal nor the relay-side cancel_run (which dispatches BrowserCancel to the extension and resolves the in-flight ServerInner.pending senders) will fire.
The in-flight browser actions will keep executing on the extension side and will only settle when they timeout or the extension responds, leaving RelayState.pending and ServerInner.pending occupied in the meantime. At minimum the doc comment on cancel_workflow should note it is for native runs only, and bind_run/unbind_run should document that callers are responsible for cancelling in-flight actions before unbinding.
|
| Filename | Overview |
|---|---|
| src/companion/host.rs | New CompanionRunHost async trait — clean three-method seam for embedding hosts; all methods documented, Send + Sync bounds correct. |
| src/companion/mod.rs | Re-exports CompanionRunHost; purely additive module wiring, no issues. |
| src/companion/server.rs | Core changes: adds run_host field, five new public methods (browser_relay, is_extension_connected, shared_tabs, bind_run, unbind_run), and three private dispatch helpers. Logic is clean; one design gap noted: extension-initiated WorkflowStart with run_host set receives a run_id but no RunEvent::* progress messages, since send_json is private and unexposed to the host. |
| src/main.rs | Trivial: adds run_host: None to satisfy the new required config field, preserving standalone behaviour. |
Sequence Diagram
sequenceDiagram
participant Ext as Chrome Extension
participant CS as CompanionServer
participant Host as CompanionRunHost (OpenHuman)
participant Relay as BrowserRelay (SocketRelay)
Note over CS,Host: New embedding-host path (run_host is Some)
Ext->>CS: "WorkflowStart {workflow_id, tab_id}"
CS->>Host: dispatch_start_run → host.start_run(workflow_id, tab_id, input)
Host-->>CS: run_id
CS-->>Ext: "control_ok {run_id}"
Note over Host,CS: Host must call bind_run before browser actions
Host->>CS: bind_run(run_id, tab_id)
CS-->>Host: Ok(())
Host->>CS: "browser_relay() → Arc<dyn BrowserRelay>"
Host->>Relay: execute(BrowserRequest)
Relay->>Ext: WebSocket BrowserRequest
Ext-->>Relay: BrowserResponse
Relay-->>Host: BrowserResponse
Host->>CS: unbind_run(run_id)
Note over CS,Ext: RunEvent::* messages are NOT sent when run_host is set
Ext->>CS: "WorkflowCancel {run_id}"
CS->>Host: dispatch_cancel_run → host.cancel_run(run_id)
Host-->>CS: bool
CS-->>Ext: "control_ok {cancelled}"
Reviews (2): Last reviewed commit: "feat(companion): CompanionRunHost seam f..." | Re-trigger Greptile
Optional CompanionServerConfig::run_host lets an embedding host (OpenHuman's
flows domain) own workflow listing + execution. When set, both the WS control
channel (WorkflowList/Start/Cancel) and the HTTP native endpoints delegate to it
via dispatch_{list_workflows,start_run,cancel_run}; None preserves the built-in
workflows_dir + start_workflow behaviour for the standalone CLI.
Summary
Additive public API on
CompanionServerso an embedding host (OpenHuman) can driveslug:"browser"tool calls from its own workflow runner, instead of only via the built-instart_workflow.New methods (no behaviour change to existing paths):
browser_relay(&self) -> Arc<dyn BrowserRelay>— shares the live socket; fails closed (relay_disconnected) when no extension is connected.is_extension_connected(&self) -> bool— for author-time / run-time readiness gating.shared_tabs(&self) -> Vec<SharedTab>— status snapshot.bind_run(run_id, tab_id) -> Result<(), CompanionServerError>+unbind_run(&str)— the run→tab authorization the nativestart_workflowdoes internally; an external runner must call it before executing a browser graph, or every action failstab_not_shared.Why
The relay + tab-binding were private to
start_workflow. OpenHuman runs its own tinyflows engine (viabuild_capabilities) and needs these handles to wrap the tool invoker inRoutingToolInvokerand bind the run to the user's shared tab. See the companion OpenHuman PR.Notes
feat/chrome-extensionso it lands with the browser feature; folds into the v0.6.0 release cut.cargo check --libclean.