Skip to content

feat(handoff): continue chats across agents and named accounts - #669

Open
Adam-Dalloul wants to merge 171 commits into
spacering-net:mainfrom
Adam-Dalloul:feat/agent-handoff
Open

Adam-Dalloul wants to merge 171 commits into
spacering-net:mainfrom
Adam-Dalloul:feat/agent-handoff

Conversation

@Adam-Dalloul

@Adam-Dalloul Adam-Dalloul commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Continue an existing chat with another agent or account while keeping its tab, unsent draft, title, pin, tags and visible history. The account picker, composer and conversation menus share one explicit handoff dialog. Account setup is reachable from the chat, including supported official sign-in and API-key methods.

Built-in Claude-to-Claude can use verified native resume and the shared account-transfer safeguards: source writers stop, session files and session-local tasks move, and the requested model is verified after initialization. Failed load/commit preserves the source. Live context resets are refused before copying or disconnecting the source, including when the database has already rebound to a private session ID.

Other supported local agents start a fresh native session with the complete saved Codeg conversation available as context, including tool records, exit codes and embedded images. Long histories use a read-only, conversation-scoped MCP reader; agents without wire MCP receive the local transcript. Any handoff involving a custom agent uses this saved-context path. A custom ACP session ID does not establish that a native file contains all its history. OpenClaw gateway handoff is unsupported. A different provider's private native state and context window do not transfer.

The account binding and immutable history segment commit together. Context survives restart and backup, and a failed first prompt retains it for retry. Historical @mentions do not become new routing frames; fresh mentions keep their selected native account. Older native-session copies remain marked as imported, while independent later native work remains importable. Retained custom-Claude source IDs and their continuations also stay covered by the upstream duplicate-import rule after switching agents; deleted chats and legacy entries without saved history do not retain those IDs. Per-segment usage/model information is retained. Existing frozen usage facts from earlier previews remain readable without adding overlapping transcript totals; this branch no longer writes speculative native-recovery snapshots.

Includes #919 and #472, retaining upstream's v0.41.1 OpenCode 2 support, workspace tabs, activity rendering, ACP 3.4, context-MCP stdin/stdout and CLI ownership. The filter fix #1029 and recording fix #1048 are now merged upstream and included here with the maintainer's fixes for model labels, relayed-message usage and duplicate imports. The account import paths retain those protections for Default and named accounts. Native usage recovery remains a separate policy discussion in #1047; API-list estimates shipped with #503. All previous migration IDs and published branch history remain intact.

Validation on v0.41.1: on the combined head, the production server/MCP/CLI build and all-target Clippy passed, and the complete library suite passed 6,252 of 6,253 tests. The exception is an unchanged upstream git test that expects newer git wording than the local image's git 2.39.5. Each branch also passed all-target Clippy and its focused suites on its own head. ESLint, TypeScript, the production frontend export and the full frontend suite passed (11,185 tests; five that hit the 5-second limit under full parallelism pass when their files run alone). Real HTTP/MCP/browser fixtures again cover native task restoration, unavailable-model refusal, rollback, paged history and images, account/agent switching, drafts/titles, @ routing, retry, restart, import deduplication and reset refusal, including phone and RTL layouts, plus the custom-agent recording, handoff ownership, old-database upgrade and frozen-usage fixtures. A new OpenCode 2 fixture creates named OpenCode accounts over HTTP: a saved server lands in the account's opencode.jsonc with its comments kept and no second file, a refused save leaves every config unchanged, the import scan lists that account's own OpenCode 2 session, and sign-in refuses a 1.x build. v0.41.2, released during this verification, merges with every branch without conflicts; a trial merge of the combined branch passed all-target Clippy, TypeScript and 527 of 528 overlapping chat and activity tests, and the remaining timing test times out the same way on unmodified v0.41.2 here. These use isolated synthetic data and CLI fixtures, not live sign-in/generation for every provider or OS. The runtime fixtures ran on the same production code as this head; the only later change fixed two test call sites. CI runs again on the refreshed commits.

Maintainer edits are enabled. See docs/agent-accounts.md for the flow and support boundaries. The fixtures do not establish live provider sign-in/generation for every agent or OS.

@Adam-Dalloul
Adam-Dalloul force-pushed the feat/agent-handoff branch 2 times, most recently from 622245a to 194e574 Compare September 6, 2026 22:07
A conversation can now change agents without starting over. Two paths,
decided per pair of agents:

Native (Claude to Claude, including an isolated custom Claude slot): the
session transcript is copied into the target's config dir and the target
session/loads it under the same id, so nothing is summarized. The load is
verified before the row moves; a target that opens a fresh session instead
is disconnected and the copy removed.

Summary (any other pair): the target starts a new session seeded with a
briefing built from the conversation (a digest of earlier turns, the last
turns in full, the user's note), bounded and reported as truncated when
earlier turns had to be dropped.

Either way the conversation row keeps its id: rebind_for_handoff moves it
to the new agent and session, and a conversation_handoff record keeps the
earlier segment reachable. The detail read splices each earlier segment in
front of the current session behind a divider tagged _meta["codeg.handoff"],
placed by user-turn count for a carried session.
One action from three places: the conversation header menu, the sidebar
card's context menu, and the composer's agent control of a persisted
conversation. The dialog reuses the agent selector, takes an optional note
for the next agent, and states which path the backend will take before the
user confirms (native load, briefing, or why it cannot run). On success the
open tab is swapped onto the new agent so the same conversation reopens
under it; a failure leaves the tab where it was.

The seam renders like the compaction divider: a chrome-less rule naming
both agents, whether the context moved natively or as a briefing, the note,
and the briefing folded behind a toggle. The seeded briefing never shows as
a user bubble, live or reloaded.
Adam-Dalloul and others added 25 commits October 10, 2026 16:45
Settings MCP still targets the built-in family row. Extra custom
agents whose spec.env sets CLAUDE_CONFIG_DIR, CODEX_HOME, GROK_HOME,
GEMINI_CONFIG_DIR, or OPENCODE_CONFIG_DIR now receive the same server
on upsert and lose it on remove.

Claude extra homes only get enabledPlugins; server defs stay in the
shared ~/.claude.json. Auth files are never copied.
Custom agents whose spec.env sets a family isolator now have a Sign in
card on the Agents page. That opens the official CLI login
(claude/codex/grok/gemini/opencode) with the isolator env set.

No tokens are copied. Account 1 stays on the built-in home. Headless
and unit-test paths return the command instead of spawning a terminal.
Settings MCP still targets the built-in family row. Extra custom
agents whose spec.env sets CLAUDE_CONFIG_DIR, CODEX_HOME, GROK_HOME,
GEMINI_CONFIG_DIR, or OPENCODE_CONFIG_DIR now receive the same server
on upsert and lose it on remove.

Claude extra homes only get enabledPlugins; server defs stay in the
shared ~/.claude.json. Auth files are never copied.
Custom agents whose spec.env sets a family isolator now have a Sign in
card on the Agents page. That opens the official CLI login
(claude/codex/grok/gemini/opencode) with the isolator env set.

No tokens are copied. Account 1 stays on the built-in home. Headless
and unit-test paths return the command instead of spawning a terminal.
…tches

A connection whose task ends on its own (the agent failed to load or
exited, or the task panicked) now parks its child in the manager's
draining list, as every explicit teardown does, so a missing map entry is
never taken as proof that the process stopped. The deferred cleanup is
spawned on the connection's runtime handle, because the guard drops
outside any runtime context. Account switches wait for every writer of the
session before copying it and before rolling a target back, and a child
parked after its task finished leaves the list as soon as it is reaped.

A switch's staging copies are removed once the switch is committed, a
rollback completes, or staging itself fails. They are kept only when a
rollback fails.
Launching a named account re-applies the shared MCP servers to config
files that the account's own CLIs rewrite too. The config writers now
leave a file alone when its content would not change, and the config
staleness check no longer syncs account folders at all.

Pi accounts mirror pi's own server list instead of the empty ACP
forwarding view, which removed their servers again on every launch. A
missing account folder no longer fails the import scan or every MCP
change; that account is skipped until its folder is restored.
…ount

A conversation switched to another account keeps its session in that
account's folder. Work-task retries, returns and merges, their
fresh-session fallback, and chat-channel resumes now launch with the
conversation's saved account instead of the Default one, which the prompt
gate then refused.
Named OpenCode accounts follow OpenCode 2: their history parser resolves the account's own database and config through upstream's rules, MCP fan-out and its preflight use the config file OpenCode loads last (JSONC included), and a change to an account's opencode.jsonc marks its session restart-required. The MCP config lock now wraps upstream's combined Antigravity/OpenCode preflight.
…sions open

OpenCode 2 keeps credentials in the database in its data dir rather than auth.json, so the sign-in also carries an OPENCODE_DB the slot sets.
…lt account

The provider model list and the OpenCode credentials helper build the default account's launch environment; named accounts sign in through their own CLI.
OpenCode 2 reads skills from both skills/ and skill/ under its config directory, so a named account links each of the three shared roots.
OPENCODE_CONFIG_DIR replaces OpenCode 2's default global directory, so an extra slot pointed there shares Default's config and the fixed ~/.config/opencode it replaced is an account of its own.
OpenCode 2 keeps credentials in its own database and no longer reads what 1.x signs in to, so a named account's sign-in refuses a 1.x build with the session's upgrade message.
OpenCode 2 stores credentials in its own database and reads auth.json only while migrating a 1.x install, so the quota panel asks OpenCode's credential helper for the account's key, caching it until codeg changes OpenCode credentials or a sign-in. A provider binding OpenCode ignores no longer hides its subscription usage.
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.

2 participants