Skip to content

feat(companion): expose relay/tab handles for embedding hosts (OpenHuman wiring) - #24

Draft
graycyrus wants to merge 2 commits into
feat/chrome-extensionfrom
oh/chrome-ext-relay
Draft

feat(companion): expose relay/tab handles for embedding hosts (OpenHuman wiring)#24
graycyrus wants to merge 2 commits into
feat/chrome-extensionfrom
oh/chrome-ext-relay

Conversation

@graycyrus

Copy link
Copy Markdown
Collaborator

Summary

Additive public API on CompanionServer so an embedding host (OpenHuman) can drive slug:"browser" tool calls from its own workflow runner, instead of only via the built-in start_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 native start_workflow does internally; an external runner must call it before executing a browser graph, or every action fails tab_not_shared.

Why

The relay + tab-binding were private to start_workflow. OpenHuman runs its own tinyflows engine (via build_capabilities) and needs these handles to wrap the tool invoker in RoutingToolInvoker and bind the run to the user's shared tab. See the companion OpenHuman PR.

Notes

  • Targets feat/chrome-extension so it lands with the browser feature; folds into the v0.6.0 release cut.
  • Purely additive; cargo check --lib clean.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7cd77a39-e25d-43bb-94b2-a28df3bb4e11

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

Comment thread src/companion/server.rs
Comment on lines +184 to +204
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);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 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.

@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown

Greptile Summary

This PR exposes five new public methods on CompanionServerbrowser_relay, is_extension_connected, shared_tabs, bind_run, and unbind_run — so an embedding host (OpenHuman) can drive slug:\"browser\" tool calls from its own workflow engine without going through the built-in start_workflow. It also introduces the CompanionRunHost trait and three private dispatch_* helpers that gate all workflow-listing and run-start/cancel paths through the host when one is configured.

  • CompanionServerConfig gains an optional run_host field; None preserves all existing standalone behaviour (verified by main.rs which sets it to None).
  • browser_relay() returns a shared Arc<dyn BrowserRelay> backed by the live SocketRelay; bind_run / unbind_run mirror the tab-registration that start_workflow does internally, exposing it to external runners.
  • One design gap noted: when run_host is Some and the extension triggers a WorkflowStart, dispatch_start_run delegates to the host but the built-in RunEvent stream (Started, StepStarted, Completed) is never emitted, since send_json is private. This may be intentional if OpenHuman sends run events via a separate channel."

Confidence Score: 5/5

Safe to merge — all changes are purely additive to an existing branch, the default (run_host: None) path is unaffected, and no existing behaviour is mutated.

The new public API is strictly additive and well-documented. The existing standalone path is preserved exactly — main.rs passes run_host: None, all dispatch helpers fall through to the original methods. The one noted gap (run events not forwarded when the host handles a workflow start) is a deliberate design trade-off, not a defect in the changed path.

Files Needing Attention: No files require special attention beyond the clarifying question on dispatch_start_run regarding extension-side run events.

Important Files Changed

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}"
Loading

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.
@graycyrus
graycyrus marked this pull request as draft July 29, 2026 16:51
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.

1 participant