Skip to content

fix(remote): route bot device controls remotely and let the Agent list devices and dispatch - #3652

Merged
bobleer merged 5 commits into
GCWing:mainfrom
bobleer:bob/remote-device-agent-gaps
Oct 31, 2026
Merged

bobleer merged 5 commits into
GCWing:mainfrom
bobleer:bob/remote-device-agent-gaps

Conversation

@bobleer

@bobleer bobleer commented Oct 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two remote-device gap families found from a WeChat-driven CLI daemon:

  1. IM bot on a remote device. With another device selected in the bot's device menu, /model failed with Session storage is unavailable: <id> because the bot read and changed models through this host's session store. The same local-only pattern also broke assistant mode: "switch assistant" claimed no assistants existed, new assistant sessions landed in the device's open project workspace (or failed when none was open), and resume showed no transcript preview.
  2. The Agent could not see devices or dispatch. feature.detached-dispatch had only "open UI" items, so on CLI the whole capability reported "no live presentation surface", and no product-control operation returned the account's device list. The Agent answered "only one device" from Remote Connect status, which describes just the executing host.

Type and Areas

Type: bug fix / feature

Areas: Rust core (remote-connect bot, dispatch, product-control tool), product-domains contracts, CLI, desktop/Tauri, generated capability catalog and docs, agent prompts

Motivation / Impact

Bot (remote_connect/bot/command_router.rs)

  • /model reads get_model_catalog and applies set_session_model on the selected device. reasoning_preset is omitted because an explicit null would clear it.
  • Assistant mode lists and selects the device's assistants (list_assistants) without switching the device's own active workspace.
  • Session workspaces are chosen per mode, as on the local host: the selected assistant, else the selected project, else the device's open project. An unselected assistant session lets the device use its default assistant, and the bot remembers where new sessions land.
  • Resume previews come from the device transcript, and list failures say "failed to list sessions" instead of "failed to create session".
  • Remote error replies are decoded as typed RemoteResponse errors.

Agent device list and dispatch

  • New closed contract detached_dispatch_control.rs and owner facts on feature.detached-dispatch:
    • list-targets (Read): every same-account device with isCurrentDevice, online state, kind, model and OS, plus saved SSH targets.
    • submit (Execute): workspacePath is scoped ProductHostLocal, approvalPolicy must be explicit.
    • status, list-jobs (Read) and cancel (Destructive).
  • Core execute_dispatch_control is shared by the Desktop binding, the CLI binding and the existing openbitfun dispatch outbound command. Hosts supply only their account session and SSH manager.
  • Targets resolve a device name, alias, device:<id> or ssh:<id>. This host is never a target, and offline devices fail without a local fallback.
  • Inside a detached dispatch worker, the generated catalog and the CLI port both refuse submit, so a job cannot fan out further dispatches.
  • Desktop runs dispatch operations outside the product-settings transaction, because a submit can spend minutes uploading its baseline.
  • claw_mode.md, openbitfun_agent.md and the OpenBitFunControl description tell the Agent to read list-targets before stating a device count, and to dispatch only on request.

Remote scenarios: remote control (IM bot driving another device) and Detached Dispatch (controller side, via the Agent) are the targets of this change. Peer Device Mode keeps requiring product_control_native_v1 for the new provider, which CLI peers do not advertise, so it stays explicitly unavailable there. The dispatch wire protocol (v6) and all target verbs are unchanged. Bot changes use only existing RemoteCommands, so older target hosts keep working.

Upgrade compatibility: no persisted shapes change. The dispatch outbound targets output now uses the shared list-targets shape introduced earlier this week.

Verification

  • Loopback encrypted relay fixture driving the real bot flows against a fake device (remote_device_flow_tests): /model list and apply; assistant switching; resume with preview; assistant session creation without a workspace. All bot tests: core 76, services-integrations 71.
  • cargo test -p openbitfun-product-domains --no-default-features (contract risk, scope and per-profile availability, including the refused nested submit)
  • cargo test -p openbitfun-core --lib --features agent-runtime,ssh-remote,remote-connect,dispatch-store service::dispatch::agent_control
  • cargo test -p openbitfun-core --lib --no-default-features --features mcp-runtime,git agentic::tools::implementations::openbitfun_control_tool::tests
  • cargo test -p openbitfun-desktop --lib -- openbitfun_control_host::tests dispatch_api (catalog-to-Desktop binding guard)
  • cargo test -p openbitfun-cli --bin openbitfun -- dispatch remote_control, including a new guard that every CLI-available provider operation has a CLI binding
  • cargo test -p openbitfun-agent-content --test prompt_catalog_contracts
  • pnpm run capabilities:check, pnpm run check:core-boundaries (+ :test), pnpm run check:repo-hygiene, web-ui openBitFunControlDiscovery and ProductControlAPI tests
  • Real process: openbitfun dispatch outbound targets signed out (empty devices with a reason) and on a signed-in account (4 devices, the executing Mac marked current, offline devices flagged, SSH targets listed). No live WeChat run or real cross-device submit was performed.

Checked refreshed upstream main and open PRs before implementation; none covered these gaps.

Checklist

  • This PR is focused and does not include secrets, temporary prompts, generated scratch files, or unrelated artifacts.
  • Relevant verification is recorded above, or skipped checks are explained.
  • User-facing strings, docs, and locales are updated where applicable.

…the selected device

With a remote device selected, the IM bot still read and changed models
through this host's session store ("Session storage is unavailable"), told
assistant-mode users that no assistants existed, created assistant sessions
in the device's open project workspace (or failed when none was open), and
resumed sessions without the transcript preview.

Route /model through get_model_catalog and set_session_model on the device,
list and select the device's assistants, choose session workspaces per mode
(selected assistant, selected project, then the device's open project), let
the device place an unselected assistant session in its default assistant,
remember where new sessions land, and preview resumed sessions from the
device transcript. Remote error replies now surface as errors.
…sktop and CLI

Detached Dispatch was reachable only through Desktop UI flows: the product
control catalog had no executable dispatch operations, CLI marked the whole
capability as lacking a presentation surface, and no operation returned the
account's device list, so the Agent could neither count the user's devices
nor dispatch work.

Add the detached-dispatch product-control provider on
feature.detached-dispatch (list-targets, submit, status, list-jobs, cancel)
with a closed request contract. Both hosts bind it to a new Core executor,
execute_dispatch_control, which the CLI dispatch outbound command now also
uses. list-targets returns every same-account device with isCurrentDevice
plus saved SSH targets; submit resolves a device name, alias, device:<id>, or
ssh:<id>, never targets this host, and never falls back to a local run.
Detached dispatch workers may observe but not submit further jobs. Desktop
runs dispatch operations outside the settings transaction because a submit
can spend minutes uploading its baseline.
Tell the Claw and OpenBitFun agents, and the OpenBitFunControl description,
that same-account devices and Detached Dispatch live under
feature.detached-dispatch, to read list-targets before stating a device count,
and to submit only on the user's request without falling back to this host.
…gent-gaps

# Conflicts:
#	docs/interactive-capabilities/README.md
#	docs/interactive-capabilities/capabilities.json
#	docs/interactive-capabilities/technical/product-control-open-audit.json
#	docs/interactive-capabilities/technical/tauri-command-map.json
#	src/crates/contracts/product-domains/src/generated/product-control-catalog.json
#	src/web-ui/src/app/global-search/generated/interactive-capabilities.json
#	src/web-ui/src/infrastructure/api/generated/productControl.ts
@bobleer
bobleer merged commit c280299 into GCWing:main Oct 31, 2026
13 checks passed
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