Skip to content

[Bug][Mobile]: New-tab agent menu resolves detection from repo.connectionId only, so runtime-hosted workspaces list the paired host's agents #13752

Open

Description

@sysilion-ib

Summary

The mobile new-tab agent menu resolves the agent-detection host from repo.connectionId only. Runtime-hosted repos have connectionId: null, so they fall through to preflight.detectAgents, which runs on the paired host rather than on the runtime environment that owns the workspace. The menu then lists the paired host's installed agents; agents installed only on the runtime host never appear, and when the paired host has no agents at all the section is empty.

This is the mobile counterpart of #9370, which was fixed for desktop by #9790 (AgentsPane + QuickLaunchButton). The mobile loader was never extended, so it still has the SSH-only resolution that #9790 replaced.

Code references are from main @ c065a2d (2026-08-10).

Where it goes wrong

mobile/src/session/mobile-new-tab-agent-loader.ts declares a repo shape that cannot express runtime ownership:

// :11-14
type RuntimeRepoSummary = {
  id: string
  connectionId?: string | null
}

and then branches on that field alone:

// :58-61
const connectionId = repo.connectionId?.trim() || null
return connectionId
  ? client.sendRequest('preflight.detectRemoteAgents', { connectionId })
  : client.sendRequest('preflight.detectAgents')   // runtime-hosted repos land here

repo.list returns full Repo rows (src/main/runtime/rpc/methods/repo.ts:109-114 → runtime.listRepos()), so the field that disambiguates this case is already on the wire — and its doc comment names this exact failure mode:

// src/shared/types.ts:269-273
/**
 * Explicit execution owner for this repo. Runtime-host repos need this
 * because they otherwise look identical to local repos (`connectionId: null`).
 */
executionHostId?: 'local' | `ssh:${string}` | `runtime:${string}` | null

Desktop resolves all three owners from that field:

// src/renderer/src/hooks/useAgentDetectionTarget.ts:53-60
const executionHost = parseExecutionHostId(getExecutionHostIdForWorktree(state, worktreeId))
if (executionHost?.kind === 'ssh') {
  return `ssh:${executionHost.targetId}`
}
if (executionHost?.kind === 'runtime') {
  return `runtime:${executionHost.environmentId}`
}
return AGENT_DETECTION_LOCAL_TARGET_KEY

The mobile loader has no runtime branch, and the fallback is silent — there is no signal distinguishing "the runtime host reports no agents" from "we asked the wrong host".

Steps to reproduce

  1. Run orca serve on a headless host B and install an agent there that is not installed on your desktop A (e.g. pi).
  2. On desktop A, register B as a Remote Orca Server environment and open a workspace that lives on B.
  3. Pair Orca Mobile (Android or iOS) to A.
  4. In the mobile app, open that workspace and tap + to open the new-tab drawer.

Actual: the agent section lists A's installed agents. pi (installed only on B) is absent. If A has no agents installed, the section is empty.

Expected: the agent section lists the agents installed on B, matching what desktop A shows for the same workspace after #9790.

Note on the fix

The loader cannot fix this on its own. preflight.detectAgents takes no params (src/main/runtime/rpc/methods/preflight.ts:28-31, params: null) and mobile's RpcClient addresses only the single paired host (mobile/src/transport/rpc-client.ts:972), so there is currently no way for mobile to target a specific runtime environment. Desktop sidesteps this because its renderer dials each environment directly (src/renderer/src/store/slices/runtime-detected-agents.ts:69-72 via callRuntimeRpc({ kind: 'environment', environmentId })).

Making mobile correct therefore needs a host-side delegation step. The transport already exists — callRuntimeEnvironment(userDataPath, selector, method, params, ...) (src/main/ipc/runtime-environment-transport-routing.ts:78) — but rpc/methods/ does not currently perform environment delegation anywhere (no userDataPath usage in that layer), so which layer should own it, how to prevent delegation loops, and whether a mobile client may name an arbitrary environment all look like maintainer calls rather than something to settle in a drive-by patch. Filing this instead of a PR for that reason.

Two smaller things worth folding in whatever the shape of the fix:

  • executionHostId should be added to RuntimeRepoSummary and preferred over connectionId, so the resolution cannot drift from desktop again.
  • Any failure in the loader currently blanks the whole section: loadMobileNewTabAgentOptions throws on a repo.list/settings.get failure or on worktree_repo_not_found (mobile-new-tab-agent-loader.ts:29-34, 55-57), and the call site maps that to an empty list (mobile/app/h/[hostId]/session/[worktreeId].tsx:3747-3751). A wrong-host result and a hard failure are indistinguishable to the user.

Existing coverage in mobile/src/session/mobile-new-tab-agent-loader.test.ts has only the floating-workspace (local) and SSH cases — a runtime-owned case would be the natural regression test.

Related

Activity

  1. AmethystLiang commented on Aug 11, 2026

    @AmethystLiang
    Contributor

    thanks for reporting. looking into it

  2. AmethystLiang commented on Aug 11, 2026

    @AmethystLiang
    Contributor

    thanks for reporting. looking into it

  3. OrcaWin commented on Aug 12, 2026

    @OrcaWin
    Collaborator

    Implemented in PR #13977: #13977

    Mobile new-tab agent detection now follows the workspace execution owner: runtime-hosted repos proxy detection to the registered runtime, SSH repos use the SSH target, and local repos stay on the paired host. The new optional RPC preserves mixed-version behavior by falling back to the previous paired-host detection when an older host reports the method unavailable.

    Focused host/mobile tests, Node and mobile typechecks, static analysis, packaging, Git compatibility, and cross-version wire checks pass. The full mobile job is blocked by an unrelated existing mobile-create-pr-action Bitbucket expectation that fails identically against unchanged base code; it was reproduced locally and the failed job was rerun.

  4. added theissue type on Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions