Skip to content

feat(desktop): keep multiple Runtime Hosts connected - #3097

Merged
M4n5ter merged 18 commits into
mainfrom
feat/multi-host-desktop-sessions
Aug 16, 2026
Merged

feat(desktop): keep multiple Runtime Hosts connected#3097
M4n5ter merged 18 commits into
mainfrom
feat/multi-host-desktop-sessions

Conversation

@M4n5ter

@M4n5ter M4n5ter commented Aug 16, 2026

Copy link
Copy Markdown
Member
English

Summary

  • Keep Local and every enabled remote Runtime Host connected independently, so one unavailable remote does not block Desktop or Local work
  • Aggregate Host-labelled Session summaries while preserving stable (Host, Session) identity and routing every existing Session action, event, diagnostic, Browser, and Computer Use resource back to its owner
  • Separate enabled Hosts from the default Host; the default now governs only new and otherwise unscoped work, while CLI and TUI remain single-Host clients

Refs #2522

Verification

  • Tested against a real Runtime Host over SSH on a LAN device: Local and Remote stayed usable together; equal raw Session IDs remained distinct; exact rename/routing, remote outage, cached summaries, reconnect, disable, and re-enable behaved as intended
  • npm test -w @maka/desktop — 862 passed
  • npm test -w @maka/ui — 167 passed
  • Desktop and UI typecheck, Desktop production build, repository lint/format, Desktop/UI knip, and git diff --check passed
中文

摘要

  • Local 与每个已启用的 remote Runtime Host 独立保持连接,单个远端不可用不会阻塞 Desktop 或本地工作
  • 聚合带 Host 标识的 Session summary,以稳定的 (Host, Session) 身份隔离同 ID Session,并将已有 Session 的操作、事件、诊断、Browser 与 Computer Use 资源严格路由回所属 Host
  • 将“已启用 Host”与“默认 Host”分开;默认 Host 只负责新任务及未指定 Host 的操作,CLI 与 TUI 仍保持单 Host 模型

关联 #2522

验证

  • 通过局域网设备上的真实 SSH Runtime Host 验证:Local 与 Remote 可同时使用;相同 raw Session ID 保持隔离;精确重命名与路由、远端掉线、缓存 summary、重连、停用及重新启用均符合预期
  • npm test -w @maka/desktop — 862 项通过
  • npm test -w @maka/ui — 167 项通过
  • Desktop/UI typecheck、Desktop production build、仓库 lint/format、Desktop/UI knip 与 git diff --check 均通过

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex assisted with implementation, tests, documentation, and validation. The commit includes the required Generated-by trailer.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1b5d2e3d-f844-4b70-bbc5-61903f0bb6f0

📥 Commits

Reviewing files that changed from the base of the PR and between a4f1560 and 432d8a9.

📒 Files selected for processing (4)
  • apps/desktop/src/main/__tests__/multi-host-thread-search.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-profile-service.test.ts
  • apps/desktop/src/main/runtime-host-profile-service.ts
  • apps/desktop/src/renderer/use-shell-connections.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/desktop/src/main/tests/multi-host-thread-search.test.ts
  • apps/desktop/src/main/tests/runtime-host-profile-service.test.ts
  • apps/desktop/src/renderer/use-shell-connections.ts
  • apps/desktop/src/main/runtime-host-profile-service.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 0 remain after this review.


📝 Walkthrough

Summary

This PR allows Desktop to keep Local and multiple remote Runtime Hosts connected independently. Failed remote Hosts no longer block Local work. Sessions use stable (Host, Session) identities, with all Session actions and Host-owned resources routed to the owning Host.

The default Host applies only to new or unscoped work. Existing Sessions remain attached to their owning Host. CLI and TUI retain single-Host behavior.

Design assessment

  • The PR extends existing Runtime Host, session, IPC, preload, and UI contracts. It replaces the single-active-Host model rather than creating a parallel path.
  • The multi-target manager, target epochs, scoped routing, session projection, and profile persistence are necessary to prevent cross-Host collisions and stale requests.
  • Obsolete selection, cleanup, and single-owner APIs were removed. Further deletion should not remove the new regression coverage or scope validation.
  • Preference recovery preserves saved configuration when durable preferences cannot be read.
  • The added complexity is required because the feature changes Host lifecycle, routing, persistence, IPC, and Desktop UI behavior together.

Validation

  • Real SSH-based multi-Host testing verified simultaneous Local and remote operation.
  • Desktop/UI tests, typechecks, production build, linting, formatting, knip, and git diff --check passed according to the PR description.
  • Tests cover session projection, target routing, concurrent enablement, remote failure isolation, target replacement, transcript references, profile migration, default recovery, multi-Host search, connection snapshots, remount cleanup, and stale-session handling.
  • Visual verification showed Local and remote Sessions together with explicit Host labels.
  • The current checkout has no uncommitted diff, so the listed checks remain unverified from direct command output.

Review-relevant risks

  • Preload and IPC contracts changed across Session, connection, memory, readiness, Runtime Host profile, and event APIs. Material public-contract changes require independent human review under repository policy.
  • Desktop user-visible behavior changed through separate enabled/default profile state, unavailable profile visibility, and Host labels. Material user-visible changes require independent human review under repository policy.
  • Runtime Host preference persistence changed to schema version 2. Migration, recovery, unreadable-preference handling, and credential handling require independent human review under repository policy.
  • Multi-Host routing changes Session identity, event projection, resource ownership, and failure handling. Material behavior changes in these areas require independent human review under repository policy.
  • The person performing the merge reviews the final diff. A maintainer makes the final determination.

Walkthrough

Desktop Runtime Host support now manages multiple local and remote targets concurrently. Sessions, IPC, transcripts, browser state, connections, and renderer data now use host-qualified identities and session-aware routing.

Changes

Multi-host runtime and session routing

Layer / File(s) Summary
Scoped identity and public contracts
apps/desktop/src/shared/*, apps/desktop/src/preload/bridge-contract.d.ts
Added host-qualified session keys, target scopes, projected session summaries, connection snapshots, and multi-profile bridge contracts.
Runtime Host manager lifecycle
apps/desktop/src/main/runtime-host-desktop-manager.ts, apps/desktop/src/main/runtime-host-profile-service.ts, apps/desktop/src/main/runtime-host-boot.ts
Replaced single-target ownership with concurrent local and remote target management, profile enablement, default-profile selection, scoped lifecycle handling, and serialized mutations.
Scoped IPC and session projection
apps/desktop/src/main/runtime-host-reconnecting-ipc-main.ts, apps/desktop/src/preload/preload.ts, apps/desktop/src/renderer/desktop-transcript-range-store.ts
Validated target epochs, routed requests by host and session, projected session identifiers, and isolated transcript data by owning Host.
Renderer and settings integration
apps/desktop/src/renderer/app-shell.tsx, apps/desktop/src/renderer/settings/*, apps/desktop/src/renderer/use-shell-connections.ts
Aggregated sessions across Hosts, routed active-session data, updated profile controls, and displayed remote profile metadata.
Validation and supporting updates
apps/desktop/src/main/__tests__/*, packages/ui/src/session-history-list.tsx, docs/architecture/*, docs/runtime-host-remote-access*
Added coverage for multi-target lifecycle and projection behavior, exposed session metadata in UI rows, updated selectors, and revised architecture documentation.

Estimated code review effort: 5 (Critical) | ~120 minutes

Merge Risk: 🟡 Moderate · up to 432d8

This PR changes Desktop to aggregate and route work across multiple Runtime Hosts, but unresolved issues can hide remote session search results, discard refreshed model choices, emit incorrect profile-change state, or misroute sessions if identity formats drift; merge is not ready until these bounded correctness and validation risks are fixed or explicitly accepted.

Sequence Diagram(s)

sequenceDiagram
  participant Renderer
  participant Preload
  participant RuntimeHostManager
  participant RuntimeHost

  Renderer->>Preload: Invoke session operation with desktop session ID
  Preload->>Preload: Resolve host and local session ID
  Preload->>RuntimeHostManager: Route operation with target scope
  RuntimeHostManager->>RuntimeHost: Execute operation on owning Host
  RuntimeHost-->>RuntimeHostManager: Return Host-local result
  RuntimeHostManager-->>Preload: Return scoped result
  Preload-->>Renderer: Project Host-qualified session data
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: keeping multiple Runtime Hosts connected in Desktop.
Description check ✅ Passed The description follows the template, explains the behavior change, references the issue, documents verification, AI use, and checklist status.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Ai Use Disclosure ✅ Passed The PR selects substantive generative use, names OpenAI Codex and its scope, and all seven introduced commits contain standalone Generated-by trailers naming Codex/OpenAI Codex.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/multi-host-desktop-sessions

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

@M4n5ter

M4n5ter commented Aug 16, 2026

Copy link
Copy Markdown
Member Author

Visual verification

Local and remote Sessions are shown together, while remote rows retain an explicit Host label. The screenshot was captured from the real two-Host SSH smoke test described in the PR body.

本地与远程 Session 同时展示,远端条目保留明确的 Host 标识。截图来自 PR body 所述的真实双 Host SSH smoke test。

Desktop showing Local and LAN Linux remote Sessions together

Automated by OpenAI Codex.

@M4n5ter
M4n5ter force-pushed the feat/multi-host-desktop-sessions branch from 9b18ee9 to 74e8319 Compare August 16, 2026 04:34
@M4n5ter
M4n5ter force-pushed the feat/multi-host-desktop-sessions branch 2 times, most recently from c33d0da to 5ed2f08 Compare August 16, 2026 05:05
@M4n5ter
M4n5ter marked this pull request as ready for review August 16, 2026 05:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (10)
apps/desktop/src/main/runtime-host-reconnecting-ipc-main.ts (1)

183-192: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Read the epoch handler once.

slot.handlers.get(epoch) runs three times and forces a non-null assertion. One lookup removes the assertion and the repeated map reads.

♻️ Proposed simplification
-    if (
-      slot.handlers.get(epoch) !== undefined &&
-      slot.handlers.get(epoch) !== previous
-    ) {
-      return Promise.resolve(slot.handlers.get(epoch)!);
-    }
+    const current = slot.handlers.get(epoch);
+    if (current !== undefined && current !== previous) {
+      return Promise.resolve(current);
+    }
apps/desktop/src/renderer/desktop-transcript-range-store.ts (1)

341-358: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the shared Host/Session key codec.

Import desktopSessionKey and parseDesktopSessionKey from ../preload/runtime-host-identity.js. Adapt the parser result from a tuple to { hostId, sessionId }, then remove both local helpers.

apps/desktop/src/main/runtime-host-native-capabilities.ts (1)

81-81: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Encode the additionalServices / targetScope pairing in the types.

targetScope stays optional, but requireTargetScope turns a missing value into a runtime throw whenever input.additionalServices exists. A discriminated options type, or passing the scope as a second argument of additionalServices from a required field, would move this to compile time. The single production caller already supplies both, so this is preventive only.

Also applies to: 98-101, 185-188

apps/desktop/src/preload/desktop-session-projection.ts (1)

106-109: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the optional wrapper and the non-null assertions.

Each call site already guards === undefined before spreading, so projectRelatedSessionId never receives undefined and the two ! operators are noise.

♻️ Proposed simplification
-  const projectRelatedSessionId = (value: string | undefined): string | undefined =>
-    value === undefined
-      ? undefined
-      : projectSessionId(host, value);

Then call projectSessionId(host, …) directly at each guarded site and remove the ! operators.

As per path instructions: "Flag concrete cases where code can be deleted or simplified."

Also applies to: 127-127, 135-135

Source: Path instructions

apps/desktop/src/main/__tests__/runtime-host-profile-service.test.ts (2)

46-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the parsed field, not the serialized text.

The regex /"schemaVersion": 2/ depends on the writer's JSON indentation. A change from JSON.stringify(value, null, 2) to any other spacing breaks this test without any behavior change.

♻️ Proposed fix
-  assert.match(
-    await readFile(join(root, "runtime-host-profile-selection.json"), "utf8"),
-    /"schemaVersion": 2/,
-  );
+  assert.equal(
+    JSON.parse(await readFile(join(root, "runtime-host-profile-selection.json"), "utf8"))
+      .schemaVersion,
+    2,
+  );

As per path instructions: "Flag tests that duplicate existing coverage, assert implementation details, or do not protect observable behavior."

Source: Path instructions


165-166: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move removal coverage into its own test.

The test is named "separates enabled Hosts from the default Host". Lines 165-166 then exercise service.remove, which is a different behavior.

remove in apps/desktop/src/main/runtime-host-profile-service.ts has three rules: reject the local profile, reject an enabled profile, and reject the default profile. Only the happy path is exercised here, as a tail of an unrelated test. The three rejection rules have no coverage.

Extract a removes a disabled remote Host test, and add cases for the three rejections.

As per path instructions: "Flag tests that duplicate existing coverage, assert implementation details, or do not protect observable behavior."

Source: Path instructions

apps/desktop/src/main/__tests__/runtime-host-native-capabilities.test.ts (1)

152-169: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a case for the missing targetScope.

createDesktopNativeCapabilityProvider now calls requireTargetScope(providerOptions.targetScope) whenever additionalServices is present (apps/desktop/src/main/runtime-host-native-capabilities.ts Line 98-100). That call throws when targetScope is absent.

This test only covers the success path. A caller that supplies additionalServices without targetScope now fails at construction time, and no test protects that contract.

Add a case that constructs the provider with additionalServices and no targetScope, and asserts the throw.

As per path instructions: "Flag tests that duplicate existing coverage, assert implementation details, or do not protect observable behavior."

Source: Path instructions

apps/desktop/src/renderer/locales/settings-projects-copy.ts (1)

44-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename runtimeHost.active to defaultBadge.

The badge renders for entry.isDefault, and both locale values say “Default.” Update the type, locale objects, and consumer.

apps/desktop/src/renderer/settings/runtime-host-profiles-section.tsx (1)

300-306: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Explain why the default Runtime Host is disabled.

When entry.isDefault is true, pass a localized reason through the supported disabledMessage prop. Switch already uses this prop elsewhere; do not rely on a tooltip or title.

apps/desktop/e2e/streaming-remount.spec.ts (1)

9-18: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Preserve Playwright auto-waiting in sessionRow

Locator.evaluateAll queries the current DOM once. It can return no rows while the sidebar remounts, so Line 16 can throw before .click() retries. Return sidebar.locator(\[data-session-id=${JSON.stringify(sessionId)}]`)and removeawait` at the call sites.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 38a9af7b-7010-48f8-8515-e7089344e9f7

📥 Commits

Reviewing files that changed from the base of the PR and between 2666a57 and 5ed2f08.

📒 Files selected for processing (47)
  • apps/desktop/e2e/streaming-remount.spec.ts
  • apps/desktop/src/main/__tests__/app-shell-session-ui-state.test.ts
  • apps/desktop/src/main/__tests__/desktop-session-projection.test.ts
  • apps/desktop/src/main/__tests__/desktop-transcript-range-store.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-desktop-manager.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-native-capabilities.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-profile-service.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-reconnecting-ipc-main.test.ts
  • apps/desktop/src/main/__tests__/use-onboarding-snapshot.test.ts
  • apps/desktop/src/main/browser-ipc-main.ts
  • apps/desktop/src/main/desktop-diagnostics-ipc-main.ts
  • apps/desktop/src/main/main-window.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/runtime-host-desktop-candidate.ts
  • apps/desktop/src/main/runtime-host-desktop-manager.ts
  • apps/desktop/src/main/runtime-host-native-capabilities.ts
  • apps/desktop/src/main/runtime-host-profile-service.ts
  • apps/desktop/src/main/runtime-host-reconnecting-ipc-main.ts
  • apps/desktop/src/main/runtime-host-upgrade-dialog.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/desktop-session-projection.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/preload/runtime-host-identity.ts
  • apps/desktop/src/renderer/app-shell-effects.ts
  • apps/desktop/src/renderer/app-shell-session-ui-state.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/app.tsx
  • apps/desktop/src/renderer/desktop-transcript-range-store.ts
  • apps/desktop/src/renderer/locales/settings-navigation-copy.ts
  • apps/desktop/src/renderer/locales/settings-projects-copy.ts
  • apps/desktop/src/renderer/main.tsx
  • apps/desktop/src/renderer/session-message-settlement.ts
  • apps/desktop/src/renderer/settings/projects-settings-page.tsx
  • apps/desktop/src/renderer/settings/runtime-host-profiles-section.tsx
  • apps/desktop/src/renderer/settings/tasks-settings-page.tsx
  • apps/desktop/src/renderer/use-app-shell-session-list.ts
  • apps/desktop/src/renderer/use-app-shell-session-workspace.ts
  • apps/desktop/src/renderer/use-module-data.ts
  • apps/desktop/src/renderer/use-project-context.ts
  • apps/desktop/src/renderer/use-shell-connections.ts
  • apps/desktop/src/renderer/use-task-submission-readiness.ts
  • docs/architecture/runtime-host-architecture.md
  • docs/architecture/runtime-host-architecture.zh-CN.md
  • docs/runtime-host-remote-access.md
  • docs/runtime-host-remote-access.zh-CN.md
  • packages/ui/src/session-history-list.tsx
  • packages/ui/src/session-list-panel.tsx
💤 Files with no reviewable changes (5)
  • apps/desktop/src/main/tests/app-shell-session-ui-state.test.ts
  • apps/desktop/src/renderer/app-shell-session-ui-state.ts
  • apps/desktop/src/renderer/use-app-shell-session-list.ts
  • apps/desktop/src/renderer/use-module-data.ts
  • apps/desktop/src/renderer/use-app-shell-session-workspace.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 1 remains after this review.

Comment thread apps/desktop/src/main/runtime-host-boot.ts
Comment thread apps/desktop/src/main/runtime-host-boot.ts
Comment thread apps/desktop/src/main/runtime-host-profile-service.ts
Comment thread apps/desktop/src/preload/preload.ts
Comment thread apps/desktop/src/renderer/app-shell-effects.ts Outdated
Comment thread apps/desktop/src/renderer/app-shell.tsx Outdated
Comment thread apps/desktop/src/renderer/app-shell.tsx Outdated
Comment thread docs/architecture/runtime-host-architecture.md Outdated
@M4n5ter
M4n5ter force-pushed the feat/multi-host-desktop-sessions branch 4 times, most recently from 9089836 to 88a866e Compare August 16, 2026 07:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
docs/architecture/runtime-host-architecture.zh-CN.md (1)

194-200: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Document the duplicate State Root invariant.

The profile service and RuntimeHostDesktopManager reject a second enabled profile with the same rootId, including local/remote conflicts. State this invariant here and add a remote/remote regression test.

apps/desktop/src/main/runtime-host-boot.ts (1)

414-421: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Delete the redundant epoch comparison.

The lookup key at Line 415 is scope.targetEpoch, and Line 742 registers each context under its own scope.targetEpoch. Therefore target.scope.targetEpoch === scope.targetEpoch is always true here. Only the hostId check adds information.

resolveRuntimeHostDiagnostics at Lines 1152-1156 already uses the shorter form.

♻️ Proposed simplification
   isHostActive: (scope) => {
     const target = runtimePolicyTargetsByEpoch.get(scope.targetEpoch);
-    return Boolean(
-      target?.isActive() &&
-      target.scope.hostId === scope.hostId &&
-      target.scope.targetEpoch === scope.targetEpoch,
-    );
+    return Boolean(target?.isActive() && target.scope.hostId === scope.hostId);
   },

As per path instructions: "Flag concrete cases where code can be deleted or simplified."

Source: Path instructions

apps/desktop/src/renderer/use-shell-connections.ts (1)

48-63: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

The identity-preserving update ignores chatModelChoices.

The bail-out compares defaultConnection and connection slug/updatedAt only. If a Host returns the same connections with a different chatModelChoices list, the hook returns previous and discards the new choices. The model picker then shows a stale catalog until the next connection mutation. Include the choices in the comparison, or compare the whole snapshot.

Proposed fix
       setSnapshot((previous) =>
         previous.defaultConnection === next.defaultConnection &&
-        connectionsEqual(previous.connections, next.connections)
+        connectionsEqual(previous.connections, next.connections) &&
+        choicesEqual(previous.chatModelChoices, next.chatModelChoices)
           ? previous
           : next,
       );
function choicesEqual(
  a: DesktopConnectionSnapshot['chatModelChoices'],
  b: DesktopConnectionSnapshot['chatModelChoices'],
): boolean {
  if (a.length !== b.length) return false;
  return a.every((choice, index) =>
    choice.connectionSlug === b[index].connectionSlug && choice.model === b[index].model);
}

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d565591c-fbfd-4efc-8ea4-3779736ec339

📥 Commits

Reviewing files that changed from the base of the PR and between 5ed2f08 and 88a866e.

📒 Files selected for processing (42)
  • apps/desktop/e2e/streaming-remount.spec.ts
  • apps/desktop/src/main/__tests__/desktop-session-projection.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-connections-ipc-main.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-desktop-candidate.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-profile-service.test.ts
  • apps/desktop/src/main/__tests__/stale-sessions.test.ts
  • apps/desktop/src/main/__tests__/use-onboarding-snapshot.test.ts
  • apps/desktop/src/main/browser-ipc-main.ts
  • apps/desktop/src/main/desktop-diagnostics-ipc-main.ts
  • apps/desktop/src/main/main-window.ts
  • apps/desktop/src/main/onboarding-service.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/runtime-host-connections-ipc-main.ts
  • apps/desktop/src/main/runtime-host-desktop-candidate.ts
  • apps/desktop/src/main/runtime-host-desktop-manager.ts
  • apps/desktop/src/main/runtime-host-native-capabilities.ts
  • apps/desktop/src/main/runtime-host-profile-service.ts
  • apps/desktop/src/main/runtime-host-reconnecting-ipc-main.ts
  • apps/desktop/src/main/runtime-host-settings-ipc-main.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/app-shell-effects.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/desktop-transcript-range-store.ts
  • apps/desktop/src/renderer/locales/settings-projects-copy.ts
  • apps/desktop/src/renderer/settings/provider-panel-shared.ts
  • apps/desktop/src/renderer/settings/providers-panel.tsx
  • apps/desktop/src/renderer/settings/runtime-host-profiles-section.tsx
  • apps/desktop/src/renderer/stale-sessions.ts
  • apps/desktop/src/renderer/use-onboarding-snapshot.ts
  • apps/desktop/src/renderer/use-shell-chat-model.ts
  • apps/desktop/src/renderer/use-shell-connections.ts
  • apps/desktop/src/renderer/use-shell-memory-pill.ts
  • apps/desktop/src/shared/desktop-connection-snapshot.ts
  • apps/desktop/src/shared/desktop-session-projection.ts
  • apps/desktop/src/shared/runtime-host-identity.ts
  • apps/desktop/stories/settings/provider-settings.stories.tsx
  • apps/desktop/stories/settings/settings-pages.stories.tsx
  • apps/desktop/tsconfig.preload.json
  • apps/desktop/tsconfig.renderer.json
  • docs/architecture/runtime-host-architecture.md
  • docs/architecture/runtime-host-architecture.zh-CN.md
💤 Files with no reviewable changes (1)
  • apps/desktop/src/main/runtime-host-settings-ipc-main.ts
🚧 Files skipped from review as they are similar to previous changes (17)
  • apps/desktop/src/main/tests/use-onboarding-snapshot.test.ts
  • apps/desktop/e2e/streaming-remount.spec.ts
  • docs/architecture/runtime-host-architecture.md
  • apps/desktop/src/main/desktop-diagnostics-ipc-main.ts
  • apps/desktop/src/main/browser-ipc-main.ts
  • apps/desktop/src/main/runtime-host-native-capabilities.ts
  • apps/desktop/src/renderer/locales/settings-projects-copy.ts
  • apps/desktop/src/main/runtime-host-reconnecting-ipc-main.ts
  • apps/desktop/src/renderer/settings/runtime-host-profiles-section.tsx
  • apps/desktop/src/main/main-window.ts
  • apps/desktop/src/renderer/app-shell-effects.ts
  • apps/desktop/src/renderer/desktop-transcript-range-store.ts
  • apps/desktop/src/main/runtime-host-desktop-candidate.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/main/runtime-host-profile-service.ts
  • apps/desktop/src/main/runtime-host-desktop-manager.ts
  • apps/desktop/src/preload/preload.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 1 remains after this review.

Comment thread apps/desktop/src/main/runtime-host-boot.ts
Comment thread apps/desktop/src/renderer/app-shell.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
apps/desktop/src/main/__tests__/multi-host-thread-search.test.ts (1)

11-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add the "all Hosts returned errors" case.

The two assertions cover total rejection and partial success. The remaining observable branch in collectThreadSearchResponses is the one where every Host fulfills with a SearchError. That branch returns the first error object, or [] when none is found. Without a test, a regression there would return an empty result list and hide the failure from the user.

♻️ Suggested extra assertion
   assert.deepEqual(
     await collectThreadSearchResponses(
       [Promise.reject(new Error('Host A unavailable')), Promise.resolve([RESULT])],
       10,
     ),
     [RESULT],
   );
+
+  assert.deepEqual(
+    await collectThreadSearchResponses(
+      [Promise.resolve({ ok: false, reason: 'provider_error', message: 'Host A failed' })],
+      10,
+    ),
+    { ok: false, reason: 'provider_error', message: 'Host A failed' },
+  );
 });
apps/desktop/src/main/runtime-host-boot.ts (1)

676-682: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Resolve the failing profile by ID, not by isDefault.

resolveDesktopRuntimeHostStartup forces defaultProfileId to Local when the profile catalog cannot be read. In that path entry.isDefault matches the Local entry, so profileName describes Local while profileId names the unavailable remote. The fallback then shows a raw profile ID in the dialog. Matching on the profile ID is exact in every path.

♻️ Proposed change
-      const entry = snapshot.entries.find((candidate) => candidate.isDefault);
+      const entry = snapshot.entries.find(
+        (candidate) =>
+          candidate.profile.id === runtimeHostStartup.preferences.defaultProfileId,
+      );
apps/desktop/src/renderer/use-shell-connections.ts (1)

39-49: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Remove the render-time currentKey mutation.

Capture snapshotKey in setSnapshot and use snapshotKey !== key in the error guard. A discarded render can otherwise change the shared ref before a callback from the committed render writes the snapshot, which can select the wrong Host bucket.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 62f95c1c-a4d5-4436-9fce-f748ef3fb480

📥 Commits

Reviewing files that changed from the base of the PR and between 88a866e and e3e865b.

📒 Files selected for processing (15)
  • apps/desktop/src/main/__tests__/multi-host-thread-search.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-default-recovery.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-desktop-manager.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-profile-service.test.ts
  • apps/desktop/src/main/__tests__/use-shell-connections.test.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/runtime-host-default-recovery.ts
  • apps/desktop/src/main/runtime-host-desktop-manager.ts
  • apps/desktop/src/main/runtime-host-profile-service.ts
  • apps/desktop/src/preload/multi-host-thread-search.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/use-shell-connections.ts
  • docs/architecture/runtime-host-architecture.md
  • docs/architecture/runtime-host-architecture.zh-CN.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • docs/architecture/runtime-host-architecture.zh-CN.md
  • apps/desktop/src/main/runtime-host-profile-service.ts
  • apps/desktop/src/main/runtime-host-desktop-manager.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/preload/preload.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 1 remains after this review.

Comment thread apps/desktop/src/main/__tests__/use-shell-connections.test.ts Outdated
Comment thread apps/desktop/src/preload/multi-host-thread-search.ts Outdated
M4n5ter added 13 commits August 16, 2026 18:50
Keep Local and enabled remote targets independent, aggregate Sessions with stable Host-scoped identities, and route existing Session operations and Client-local resources back to their owning Host.

The default Host now controls only new and otherwise unscoped work. CLI and TUI remain single-Host clients.

Generated-by: OpenAI Codex
Keep startup interaction and connection data scoped to the intended Host, recover safely from corrupt Client preferences, and make Host metadata mandatory at the Desktop projection boundary. Preserve explicit unavailable defaults instead of silently moving new work to Local.

Generated-by: OpenAI Codex
Separate default-Host settings from the active Session Host, and derive model, memory, readiness, diagnostics, and attachment views from the owning Host. Centralize Desktop Session identity projection so raw Host-local ids cannot leak into renderer state.

Generated-by: OpenAI Codex
Restore an actionable recovery choice when the default remote Host is unavailable, while keeping other enabled Hosts usable. Preserve Host scope across reconnect diagnostics, deferred subscriptions, and connection projection changes.

Generated-by: OpenAI Codex
Keep Local usable when remote profile metadata cannot be read without erasing the saved remote enablement state. Settle fire-and-forget Host projections and preserve total thread-search failures while retaining partial results.

Generated-by: Codex
Make the bootstrap effect own its animation-frame callback so StrictMode cleanup cannot leave a second fixture or startup refresh running after remount.

Generated-by: Codex
Keep the Local startup fallback read-only when durable Runtime Host preferences cannot be read, preventing later settings actions from replacing unknown saved configuration. Commit Host projection identity only after React accepts a render, and retain structured all-Host search failures.

Generated-by: Codex
Decode active and enumerated Runtime Host identities through one metadata path so early Session creation no longer depends on cache warmup. Preserve live scopes across non-authoritative profile events and deliver scheduled-task notifications from every enabled Host.

Generated-by: Codex
Wait for the search dialog to enter its native modal state before sending Escape. CSS visibility can precede showModal(), making the prior E2E assertion timing-dependent.

Generated-by: Codex
Interleave per-Host thread matches before applying the global limit so one full result page cannot hide every later Host. Strengthen the connection projection regression to verify a cached Host returns without an empty frame.

Generated-by: Codex
Use Local for unscoped work while the preferred default Host is not available without changing the saved default. Roll back only newly created profiles whose add flow fails before connection handling, while retaining unavailable profiles for retry.

Generated-by: Codex
Treat an unreadable remote profile catalog as transient availability state rather than replacing the persisted default with Local. Local remains the effective fallback while the catalog is unavailable.

Generated-by: Codex
Keep the saved default profile as the user's durable preference when its target is removed, while publishing the removal as a default-target change so stale renderer scope is discarded and Local fallback remains usable.

Generated-by: Codex
Remove duplicated projection fixtures, direct mapper assertions, and render-level details already covered by stronger Host identity and isolation tests. Keep coverage focused on stable cross-Host boundaries and configuration safety.

Generated-by: Codex
The localized Plan Mode copy landed on main with an import from a package root that core does not export, breaking Desktop builds and Windows recovery CI. Use the existing plan and locale entry points instead.

Generated-by: Codex
Keep default-Host work, Session-owned work, and Desktop-only UI cleanup on separate routing paths. Unavailable defaults no longer silently fall back to Local, copy cleanup and Project reads retain their owning Session, and retired Hosts cannot leave an unroutable active task or Browser view behind.

Preserve Desktop Session identity through renderer projections so future Host-scoped code cannot accidentally erase the routing metadata.

Generated-by: Codex
@M4n5ter
M4n5ter force-pushed the feat/multi-host-desktop-sessions branch from f1d4049 to c5e3943 Compare August 16, 2026 11:07
Order Browser selection updates across asynchronous Session resolution so stale work cannot restore a hidden native view. Keep the Local Project catalog independent from the default and active remote Hosts for grouping and archived labels.

Generated-by: Codex
Keep Browser selection valid across Host reconnects and renderer reloads, and funnel default Project context through one latest-wins snapshot authority. Local Project row actions remain unavailable when another Host is the default, avoiding cross-catalog mutations until explicit Host selection lands.\n\nGenerated-by: Codex
@M4n5ter

M4n5ter commented Aug 16, 2026

Copy link
Copy Markdown
Member Author

I have manually verified the current revision and consider it to have reached a usable standard for the intended scope of this PR.

There are currently no existing remote Runtime Host users whose established workflow could be regressed by this change. The rollout impact is therefore controlled, while the existing local Runtime Host path remains the supported baseline.

Posted by Codex on behalf of the maintainer.

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