Repository navigation
bug(desktop): Side Conversation creation fails for sessions with linked child agents #3654
Description
What happened
On current main, opening a Side Conversation from a settled Desktop Session fails when the retained source history contains linked subagent / Agent Graph child Sessions.
The UI shows only the generic message:
Could not open the side chat. Please try again.
The underlying Runtime Host error is:
operation_unavailable:
Ordinary branches cannot share linked child Session ownership with their source
This still fails after the root Turn and all child agents have completed. Retrying later therefore does not resolve this path.
Verified against main at c79e9eb40ee4a0fcd204fb075427d635989eaaca.
How to reproduce
- Build and launch Desktop from current
main. - Start a normal conversation.
- Run a workflow that creates linked child Sessions, for example an Agent Graph/code-review flow with multiple subagents.
- Wait until the root Turn and child agents have all reached terminal state.
- Open Side Chat from the titlebar/workbar, or invoke
/side. - Observe that the Side Conversation is not created and the generic setup-error banner appears.
A normal settled conversation without linked child Sessions can create the Side Conversation successfully.
Expected behavior
A Side Conversation should open from the latest completed source Turn and use the retained parent history as reference-only context, without taking ownership of or controlling the source Session's child agents.
Actual behavior and root cause
The renderer correctly marks the request as a Side Conversation:
ensureCompanionFork()callsbranchFromTurn(..., { sideConversation: true }):
maka/apps/desktop/src/renderer/features/workbar/tools/side-chat/quote-companion-core.ts
Lines 250 to 255 in c79e9eb
created = await api.branchFromTurn(sourceSession.id, { sourceTurnId: copyAttempt.sourceTurnId, name, copyId: copyAttempt.copyId, sideConversation: true, });
The Desktop IPC adapter does not carry that intent into Runtime Host:
- it calls
copySession("branch", ...)without a Side Conversation purpose; sideConversationis used only for cleanup ownership and for addingmode:side_conversationmetadata after the copy succeeds:
maka/apps/desktop/src/main/runtime-host-session-execution-ipc-main.ts
Lines 598 to 633 in c79e9eb
ipcMain.handle( "sessions:branchFromTurn", async (event, sessionId: string, input: unknown) => { const normalized = normalizeRuntimeHostBranchFromTurnInput(input); const createBranch = () => deps.client.copySession("branch", { sourceSessionId: sessionId, targetSessionId: normalized.copyId, sourceTurnId: normalized.sourceTurnId, }); let branch = normalized.sideConversation ? await deps.sessionCopyCleanup.ownCreation( { sessionId: normalized.copyId, kind: 'branch', sourceSessionId: sessionId, sourceTurnId: normalized.sourceTurnId, ownerId: bindCopyOwner(event), }, createBranch, ) : await createBranch(); if (normalized.name || normalized.sideConversation) { branch = await deps.client.updateSessionMetadata(branch.id, { ...(normalized.name ? { name: normalized.name } : {}), ...(normalized.sideConversation ? { labels: [ ...new Set([ ...branch.labels, SIDE_CONVERSATION_SESSION_LABEL, ]), ], } : {}), });
The Host protocol has no field that distinguishes a Side Conversation from an ordinary branch:
maka/packages/runtime-host/src/protocol/session-revision.ts
Lines 38 to 43 in c79e9eb
export interface SessionConversationCopyInput { readonly sourceSessionId: string; readonly targetSessionId: string; readonly sourceTurnId: string; readonly expectedSourceRevision: number; }
Runtime Host therefore applies the ordinary-branch ownership rule and rejects the copy:
maka/packages/runtime-host/src/server/session-revision-graph-references.ts
Lines 91 to 95 in c79e9eb
if (input.kind === 'branch' && (requests.length > 0 || directChildren.length > 0)) { return failure( 'operation_unavailable', 'Ordinary branches cannot share linked child Session ownership with their source', );
The renderer then catches every creation exception and collapses it to fork_setup_failed, hiding the actionable Host code:
maka/apps/desktop/src/renderer/features/workbar/tools/side-chat/quote-companion-core.ts
Lines 250 to 257 in c79e9eb
created = await api.branchFromTurn(sourceSession.id, { sourceTurnId: copyAttempt.sourceTurnId, name, copyId: copyAttempt.copyId, sideConversation: true, }); } catch { return { status: 'error', code: 'fork_setup_failed' };
The ordinary-branch rejection is a valid safety invariant: an independent branch must not share child Session ownership with its source. The defect is that Side Conversation currently reaches that path as an ordinary branch even though its intended semantics are an ephemeral, reference-only context copy.
Suggested fix
Introduce an explicit Host-recognized copy purpose or operation for Side Conversations, rather than inferring the behavior from a label added after publication. For example:
type SessionConversationCopyPurpose =
| "ordinary"
| "side_conversation";The exact protocol shape is open to discussion, but the intent should:
- travel from Renderer → Desktop IPC → Runtime Host;
- be included in the idempotency fingerprint;
- be persisted with the conversation-copy publication metadata;
- cause the Host to atomically apply the Side Conversation label/semantics.
For retained linked-child results, the Side Conversation path should create an ownership-free historical snapshot:
- retain the terminal child result's user-visible status, summary, and bounded output needed as model context;
- do not retain control/ownership identifiers such as the source
childSessionId,runId, orresumedFromRunId; - copy and rewrite any artifact content that must remain accessible from the target Session;
- cover typed message results, RuntimeEvent function responses, archived tool results, and any copied ledger projections;
- never clone, reparent, delete, resume, or otherwise control the source child Session.
If a retained Graph, child Session, or child result is still live, return the existing retryable session_busy result. Missing or inconsistent terminal provenance should continue to fail closed.
Ordinary branches should keep their current rejection behavior, and revisions should keep their existing validated terminal-reference behavior.
Error-handling follow-up
The direct bug requires the Host copy semantics above; retrying in the renderer alone cannot fix a permanent operation_unavailable result.
The Desktop path should additionally preserve structured failure codes:
session_busy: show a waiting/retryable state and retry when the source becomes idle;operation_unavailable: show a specific unsupported-context error;- persistence/internal failures: show the generic setup error.
A later successful fork should also clear any previous setup banner.
There is a separate edge case in which latestSettledTurnId() documents “start empty” when no completed Turn exists, while the current caller returns fork_setup_failed. That can be handled alongside this work or tracked separately.
Acceptance criteria
- A settled source Session containing one or more terminal linked child Sessions can create a Side Conversation.
- The created Side Conversation contains useful historical child-result context without retaining child ownership/control references.
- Removing the source Session or the Side Conversation cannot incorrectly remove child Sessions owned by the other.
- Ordinary branches with linked children remain rejected.
- Revisions preserve their current validated terminal-reference behavior.
- A live source Graph or child returns a structured, retryable
session_busy. - Copy retries remain idempotent, and reusing the same target identity with a different copy purpose is rejected.
- Failed or interrupted creation leaves no visible/preparing orphan Session.
- A Runtime Host integration test covers at least one source Session with a terminal Agent Graph child and verifies successful Side Conversation creation plus ownership isolation.
- Renderer tests verify error-code preservation and clearing a stale setup banner after success.
Related work
- feat(desktop): add Codex-style side conversations #2428 introduced generic Side Conversations.
- feat(runtime-host): own session revision authority #1664 established Host-owned conversation copy and the linked-child fail-closed rule.
- feat(runtime-host): preserve Agent Graph history across revisions #2039 added validated terminal Agent Graph reference retention for revisions while continuing to reject ordinary branches.
- design(runtime): model subagents as linked child sessions #1366 defines linked child Session ownership and lifecycle.
- Branching a conversation with tool authority facts is rejected as an auth failure #2060 / fix(runtime): branch copied tool authority facts #2061 are a useful precedent for fixing a conversation-copy authority rejection at the typed Host seam while also correcting misleading UI error classification.
No existing public Maka issue, PR, or Discussion was found that tracks this exact Side Conversation + linked-child integration failure.

Activity
liuxiaocs7 commentedon Aug 23, 2026
I'll try to fix this problem
fix(desktop): snapshot linked children for side conversations
fix(desktop): snapshot linked children for side conversations
fix(desktop): snapshot linked children for side conversations
fix(desktop): snapshot linked children for side conversations
fix(desktop): snapshot linked children for side conversations
fix(desktop): snapshot linked children for side conversations (apache…