Skip to content

feat(mobile): add persistent thread goals across remote clients - #3224

Open
wgqqqqq wants to merge 5 commits into
GCWing:mainfrom
wgqqqqq:codex/mobile-goal-support
Open

wgqqqqq wants to merge 5 commits into
GCWing:mainfrom
wgqqqqq:codex/mobile-goal-support

Conversation

@wgqqqqq

@wgqqqqq wgqqqqq commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds persistent thread goals to the remote-control clients, so the phone
that drives a desktop/CLI session can manage that session's Goal.

  • Host (Rust): new wire command thread_goal / response, an explicit
    RemoteGoalAction (read/start/edit/pause/resume/clear), a new
    thread_goal_v1 capability, a handle_goal_command host hook with an
    explicit "unsupported" default, and the core runtime implementation.
  • Host (Rust): goal activation abandons a held interrupted turn, mirroring a
    new user message. Without this the goal's steering turn stays queued behind
    interrupted-turn recovery forever.
  • Shared KMP core: goal command/state contract, capability gate, stale-response
    fencing, and a foreground snapshot refresh.
  • Android / iOS / HarmonyOS: goal strip in the conversation, /goal composer
    commands, platform strings, a HarmonyOS preview surface, and focused tests.

Fixes: no issue; reported/driven by the mobile goal work.

Type and Areas

Type: Feature

Areas: Rust core (openbitfun-core remote-connect runtime host,
openbitfun-services-integrations wire protocol), mobile Android / iOS /
HarmonyOS, shared Kotlin Multiplatform core.

Motivation / Impact

Goal support existed only on the desktop surface. A phone driving a remote host
could watch goal-driven turns but not start, edit, pause, resume, or clear the
goal itself.

  • Goal operations are addressed by session ID and resolve that session's own
    workspace binding (including SSH bindings) and storage on the host. No
    controller filesystem path participates in the operation.
  • Hosts that do not advertise thread_goal_v1 are never probed with unknown
    commands; clients show an upgrade message instead.
  • Execution and persistence stay on the host if the phone disconnects.

Verification

Run on this machine:

  • pnpm run fmt:rs — formatted the 4 changed Rust files, no residual diff.
  • cargo test --locked -p openbitfun-services-integrations --no-default-features --features remote-connect --lib — 161 passed, 0 failed.
  • cargo test --locked -p openbitfun-core --no-default-features --features agent-runtime,remote-connect,git --lib agentic::coordination::scheduler::tests — 60 passed, 0 failed.
  • cargo test --locked -p openbitfun-core --no-default-features --features agent-runtime,remote-connect,git --lib service_agent_runtime::tests — 22 passed, 0 failed.
  • node --test src/apps/mobile/harmonyos/tools/tests/thread-goal.test.cjs — 8 passed, 0 failed.
  • pnpm run mobile:ui:check — contract and generated files in sync.
  • pnpm run harmony:architecture — contracts satisfied.
  • pnpm run theme:color-audit:all — 23 surfaces passed.

Not run on this machine (toolchain absent):

  • Shared Kotlin :core-feature:jvmTest and Android
    :app:connectedDebugAndroidTest ... ThreadGoalPanelTest — no Java runtime.
  • HarmonyOS assembleHap — no hvigor / DevEco toolchain.
  • iOS simulator build — only Command Line Tools installed, no full Xcode.
  • Live remote-host acceptance run against a real desktop host and device.

Reviewer Notes

  • pnpm run mobile:architecture currently fails on four violations in files this
    PR does not touch (ClientBuildContractTest.kt reaching the harmonyos tree,
    and default-arg public constructors in AccountUiState.kt,
    RemoteSidebarPresentation.kt, ConversationModels.kt). I reproduced the
    identical output from a pristine copy of HEAD (git archive HEAD of
    src/apps/mobile plus the checker), so it is pre-existing on main and not
    introduced here. The check is not wired into CI.
  • Upgrade compatibility: the wire additions are additive. Unknown/missing
    action/objective fields deserialize as None; a host without
    thread_goal_v1 keeps the old behavior, and the new trait method has an
    explicit unsupported default. The wire round-trip test covers both directions.
  • Pausing a goal does not cancel the current turn; that remains Stop. Completed
    and budget-limited goals can be edited but not resumed.
  • The HarmonyOS preview and Android instrumented test exercise the panel in
    isolation; neither replaces a live remote-host acceptance test.

Generated with OpenBitFun

@wgqqqqq
wgqqqqq marked this pull request as ready for review September 24, 2026 08:35
wgqqqqq and others added 3 commits September 24, 2026 17:09
Expose the host's persistent Goal to Android, iOS, and HarmonyOS through a
negotiated `thread_goal_v1` capability, so a phone can read, start, edit,
pause, resume, or clear the goal of the session it is driving.

- host: add `RemoteCommand::ThreadGoal` / `RemoteResponse::ThreadGoal` plus
  `RemoteGoalAction`, route them through `RemoteCommandRuntimeHost` with an
  explicit unsupported default, and implement them in the core runtime host.
  Goal operations resolve the session's own workspace binding and storage;
  no controller path participates.
- host: abandon a held interrupted turn when a goal is started, edited, or
  resumed, otherwise the goal's steering turn stays queued behind recovery.
  Re-delivering the same active objective no longer resets usage.
- shared: add the goal command/state contract, capability gating,
  stale-response fencing, and a foreground snapshot refresh.
- android/ios/harmonyos: add the goal strip, `/goal` composer commands, and
  platform strings, with a HarmonyOS preview and focused tests.

Co-authored-by: OpenBitFun <318544290+bitfun-ai@users.noreply.github.com>
…a checkpoint

When a controller attaches to a Turn mid-run, its projected checkpoint only
contains the rounds it observed. At completion the generation journal merge
appended the earlier, unmatched rounds after the final answer, so every
surface (desktop by array order, mobile by roundIndex) rendered the final
reply before the Turn's first rounds. Insert unmatched rounds before the next
matched round in generation order and renumber roundIndex accordingly.
…mobile goal actions from being dropped

- move interrupted-turn abandon from remote_thread_goal into the coordinator,
  running only after a goal change commits and will steer the session
- KMP/HarmonyOS: background goal refresh no longer shows busy; mutations
  queue behind in-flight mutations and supersede in-flight reads
- add THREAD_GOAL_V1 to remote connect contract tests
- tests for abandon ordering and mobile goal gating; drop unreachable close branch
@wgqqqqq
wgqqqqq force-pushed the codex/mobile-goal-support branch from b26f0cb to a60df87 Compare September 24, 2026 09:30
… a session

Copied turns kept the source runtime's recovery point and execution
generation, so a branch whose last turn was interrupted held dispatch and
could resume with the source context. Treat them as abandoned history.
… pause active goals

- KMP: goal requests use their own generation instead of the session work
  generation, so loadMore/permission/model/stop-turn no longer cancel an
  in-flight goal change; they are dropped only on disconnect or session switch
- host: remote pause requires an active goal (paused is idempotent), so a goal
  that completed meanwhile cannot be paused and revived through resume
- test: unrelated session work does not drop an in-flight goal change
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