Skip to content

feat(cloud): cloud chats find, read, message and start each other (port of upstream Home #15975) - #173

Merged
andrewcai8 merged 9 commits into
mainfrom
feat/port-home-15975
Oct 6, 2026
Merged

andrewcai8 merged 9 commits into
mainfrom
feat/port-home-15975

Conversation

@andrewcai8

Copy link
Copy Markdown
Owner

Each cloud chat on andrew.megpt.app runs on its own machine with its own T3 server, so an agent's thread tools only saw its own chat. This ports upstream pingdotgg#15975 (Home: agents manage threads across machines) and lets the host play the desktop window's role for cloud boxes.

  • feat(home): port pingdotgg/t3code#15975 at b5d05a6ebf is a faithful copy of the upstream PR (conflict resolutions listed in its message), so the daily sync can replace it once upstream merges it. The next commit only adapts it to the fork's newer base.
  • The host opens a fleet connection to each awake box with the broker token it already holds and registers the box's sibling chats. Listing uses stored chat cards and wakes nothing; reading or messaging a sleeping chat wakes exactly that one box. No new credential reaches a box.
  • t3_thread_launch aimed at cloud:new-chat starts a chat on a new cloud machine through the normal provisioning path (caps, account routing, sidebar, sleep and 30-day restore apply). Only a chat a person started can start chats, at most 4 live at a time.
  • On a box, the top-level chat may act across chats when its host is connected; subagents can't, and changes need full-access mode.

Not routed: t3_thread_wait across machines (a blocking wait would exceed the 90 s fleet limit); agents ask the other chat to reply instead. Worth sending upstream: the optional FleetEnvironment.chat summary and listThreadPage as a pure function.

🤖 Generated with Claude Code

andrewcai8 and others added 8 commits October 6, 2026 10:42
Applies the diff of upstream PR pingdotgg#15975 (base cf3e714, head
b5d05a6, 13 commits) as one commit so a later upstream sync can
replace it cleanly. Where the fork's base renamed what the PR touches,
conflicts keep the base's names and add the PR's lines; making the PR's
own files fit the new base is the next commit.

Conflicts resolved:
- apps/web/src/components/Sidebar.tsx: kept the fork's cloudMachinePill
  import beside upstream's resolveSidebarV2TopStatus. In the thread row,
  took upstream's SIDEBAR_TOP_STATUS table, added the fork's "expired"
  kind ("Removed"), kept the fork's machine-pill precedence (machine state
  outranks Woke and Done; Asleep only fills an empty slot), and kept the
  base's "Goal" label for a working thread with an active /goal.
- apps/desktop/src/window/DesktopWindow.ts and its test: kept the base's
  DesktopRendererHistory next to the PR's DesktopState.
- apps/server/src/mcp/McpHttpServer.ts: the base's layer names plus the
  PR's Home toolkit registration, after the project registration.
- apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts: the
  base's layer names plus the PR's notHomeLayer.
- apps/server/src/mcp/toolkits/orchestrator/tools.ts: the base's imports
  (request-secret contracts, effect/ai) plus the PR's
  OrchestratorMcpEnvironmentTarget and Schema.
- apps/server/src/mcp/toolkits/{project,thread}/handlers.ts: the base's
  `layer` export name, with the PR's watchLaunched and defaultThreadId
  helpers above it.
- apps/server/src/mcp/toolkits/project/handlers.test.ts: the base's
  imports and names plus the PR's Home imports and link assertion.
- apps/server/src/server.ts: the base's layer names plus the PR's HomeLayer.
- apps/web/src/components/ThreadNotificationCoordinator.tsx: the base's
  OrchestrationV2ThreadShell import plus the PR's isHomeLaunchedThreadId.
- docs/orchestration-v2/orchestrator-mcp-server.md: the base's
  projectId wording plus the PR's sentence about Home.

Every other file applied without conflict.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The fork's base moved to Effect 4.0.1 and upstream's module and layer
renames after pingdotgg#15975 was cut. The PR's own files now import effect/ai and
effect/reactivity, read ProviderRegistry from provider/ProviderRegistry.ts,
and its launch test uses ProjectHandlers.layer. No behavior changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A host that answers threads.list for a sleeping cloud chat from its stored
card needs the same filters and paging FleetService applies, not a copy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A cloud chat runs on its own box with its own T3 server, where Home is not
available. The box now accepts fleet.connect from the host that provisioned
it, recognised by its broker session's subject, and lets the box's top-level
chats route calls that name another environment through that host. Local
calls keep their usual path. Subagents stay on the box, and changes still
need a full-access/default live run, as for Home.

A cloud chat's launch in another environment goes to the host without a
Home launch id or watch; the host picks the thread id.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…er chats

Each cloud chat runs on its own box, so a chat could not see the user's
other chats or get more machines. The host now plays the desktop window's
part for its boxes. CloudFleetHost registers with every awake chat's box
as its fleet host, using the broker token the host already holds, and
offers every other cloud chat as an environment labelled with its stored
card. FleetEnvironment gains an optional chat summary for that, so listing
never wakes a machine.

A relayed call runs on the target box with that box's token. A sleeping
target is woken once (a retry joins the same wake) and threads.list for it
is answered from its card through FleetService's own page. The host answers
before the box's 90 s timeout and keeps waking a slow Mac.

One more environment starts a new cloud chat: t3_thread_launch there
provisions through environmentControl.provision with the calling chat's
provider, account, repository and branch and the message as its first
turn, the same request a client sends.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A changed chat card re-registered by opening a new WebSocket, which each
box records as a new client connection. The host now keeps one connection
per awake box and switches its registration stream on it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… box gives up

From review of the host relay:
- Only a chat a person started may start cloud chats, at most four live
  at a time. A started chat's id names the chat that started it, so the
  rule survives restarts and needs no new field.
- A new chat must run the same agent as the machine that starts it.
- A relayed call that spent its time waking a machine is reported, not
  acted on, so a late answer never makes the agent repeat a change. The
  launch's follow-up read is bounded too.
- A card's updatedAt alone no longer re-registers every box.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

⚠️ The thread fixture changed, so impact percentages are not directly comparable to the main baseline.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 5.0 KiB — 6.8 KiB ✅
Codex Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Codex Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Codex Live turn WebSocket decoded — 20.9 KiB — 29.3 KiB ✅
Codex Live turn messages — 2 — 8 ✅
Claude Total thread wire — 5.0 KiB — 6.8 KiB ✅
Claude Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Claude Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Claude Live turn WebSocket decoded — 21.2 KiB — 29.3 KiB ✅
Claude Live turn messages — 1 — 8 ✅

Baseline: dd3c754 · PR result: 0d3b462 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

… redials

From review of #173:
- The host acts only for the box's own chat (the lease owner) and reads
  that chat's modes from its stored card, not the box's claim. Changes
  and waking a sleeping chat's machine need full-access/default; other
  chats still list from cards.
- A chat has one launch in flight, claimed in one step: a repeat joins it,
  another waits. The cap counts requests still being prepared, from the
  durable store, so it survives restarts and slow launches.
- A box that refuses the fleet connection is retried after 15 s, doubling
  to 10 minutes; a connection that lasts a pass resets it.
- The ticket goes through HttpClient, the registration key through
  Schema, and the subagent test fixture is typed, for CI's typecheck.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@andrewcai8
andrewcai8 merged commit bace534 into main Oct 6, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant