Skip to content

feat(conductor): import Conductor workspaces as chat-tab threads - #199

Merged
adampeterhiggins merged 18 commits into
mainfrom
ah/import-conductor-thread
Oct 6, 2026
Merged

adampeterhiggins merged 18 commits into
mainfrom
ah/import-conductor-thread

Conversation

@adampeterhiggins

@adampeterhiggins adampeterhiggins commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Conductor workspaces hold useful history: a worktree on its own branch with several agent tabs. Until now there was no way to move one into T3 Code, and the existing Claude Code / Codex import can't see these sessions because they ran in Conductor's worktree rather than in the project folder.

What changed

Import Conductor workspaces. The Import conversation picker lists conversations and Conductor workspaces from all projects in one list, newest first, with search and a Filters menu (Project: All projects or one; Source: All / Claude Code / Codex / Conductor, defaulting to Conductor). The menu reuses the pull request picker's radio submenu. Rows name their project and import into it. The command palette entry is a plain Import conversation…. Workspaces are titled the way Conductor's sidebar titles them: the custom name, else the PR title, else the branch in words. It lists the project's active workspaces: same repository (by root or origin remote), worktree still on disk, at least one sent prompt. Importing one:

  • Creates a thread for each open tab, grouped as chat tabs. A new ThreadTabs.adopt writes the group membership before the threads exist, so clients never see the tabs as separate rows.
  • Points every tab at the workspace's branch and worktree, and carries over the workspace's pin.
  • Takes history from Conductor's conductor.db, opened read-only. Conductor stores Claude, Codex and Cursor output in one Claude-SDK shape, so a single parser keeps prompts and top-level reply text.
  • Copies images and files sent with a prompt (from the workspace's .context/attachments) into T3's attachment store and attaches them to the message. Conductor has already deleted many of these files, even in active workspaces; a missing one is named in the message ("Attachment not available: …") rather than silently dropped.
  • Shows diff comments sent to the agent (Conductor's review attachments) inside their prompt as quoted review comments. Without this they import as empty messages.
  • Binds Claude and Codex tabs to their native session so they resume. Cursor tabs have no session T3 can reach, so their history goes through the existing v1_import handoff.
  • Uses deterministic thread ids (conductor:<session>), so the picker shows Open for a workspace that was already imported.

New RPCs are conductor.listWorkspaces and conductor.importWorkspace; the service is apps/server/src/conductor/ConductorImporter.ts.

Fix: imported Claude sessions now resume on their first turn. An imported thread has no provider turns yet, so ClaudeAdapterV2 opened it with sessionId: <existing id>. The CLI rejects that with "Session ID … is already in use" (reproduced with claude -p --session-id). Imported provider threads now record nativeMetadata.importedNativeId, and ProviderTurnStartService treats a matching native thread as having turns, so the adapter uses resume. This also fixes the existing Claude Code import.

Workspace state that stays where it is: notes (.context/notes.md, todos.md) are files in the worktree the threads now run in. Almost all of Conductor's diff comments (about 1,120 of 1,130 in active workspaces) are mirrored GitHub PR review comments, which T3 reads from the PR it finds for the branch. Conductor stores terminal scrollback encrypted (iv + ciphertext), so terminal history can't be imported. Unsent local diff comments have no server-side home in T3 (review drafts live in the client's composer) and aren't imported. Archived workspaces and mobile (no import picker) are out of scope. Conductor's setup and run scripts aren't copied either; the fork already applies a repo's Conductor settings to T3 worktrees. Docs are updated in docs/fork-differences.md and docs/user/thread-sidebar.md.

Testing

  • ConductorImporter.test.ts builds a temporary Conductor database and checks transcript parsing (attachment mentions, inlined review comments), the workspace listing (hidden, empty and archived tabs or workspaces are filtered out), the tab-group import, the Claude native binding, the Cursor handoff, copying an image into the attachment store, naming a deleted file, and that re-importing writes nothing.
  • ProviderTurnStartService.test.ts checks that an imported native session's first turn is passed as nativeThreadHasTurns: true, with an unmarked control case.
  • The affected server tests (117) pass, and server and web typecheck clean.
  • The parser was run read-only against a real Conductor database: 2,391 rows became 91 messages in about 50 ms.
  • Not yet checked in a running client, so there are no before/after screenshots of the picker.

Done by Claude Opus 5.5 (1M context) in Claude Code, running inside T3 Code.

🤖 Generated with Claude Code

@adampeterhiggins
adampeterhiggins force-pushed the ah/import-conductor-thread branch 2 times, most recently from 100ad55 to 0eae800 Compare October 6, 2026 21:17
adampeterhiggins and others added 18 commits October 6, 2026 22:22
The import picker lists the project's active Conductor workspaces. Importing one
turns each open tab into a thread on the workspace's branch and worktree, grouped
as chat tabs, with history read from Conductor's database. Claude and Codex tabs
resume their agent session; Cursor tabs hand their history to the next turn.

Imported Claude threads now resume on their first turn instead of starting a new
session under an id that already exists.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Images and files sent with a Conductor prompt are copied into T3's attachment
store, and diff comments sent to the agent appear in their prompt. Files
Conductor already deleted are named in the message instead.

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

Workspaces are listed by the title Conductor's sidebar shows (custom name, PR
title, or branch in words), which also names untitled tabs on import. The import
dialog gains All / Claude Code / Codex / Conductor tabs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A Conductor workspace is imported to keep working in it, so its threads get
the un-settled override rather than the settled one the CLI importer uses.

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The import dialog shows conversations and Conductor workspaces in one list,
newest first, filtered by a small-caps source dropdown. The dropdown is a new
MenuSelect, which the agent panel's Tool calls / Transcript switch now uses too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The import dialog has a project dropdown beside the source dropdown, starting
on the project it was opened from. The command palette offers a plain
"Import conversation…" that no longer needs a project in context.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Falls back to all sources when Conductor is not on the machine.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Filters conversations by title and first prompt, and Conductor workspaces by
title, folder, branch, and tab titles.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
"All projects" heads the project dropdown and is the default from the command
palette; rows name their project and import into it. One derived atom reads
each project's listings, and Conductor workspaces shared by two projects of
one repository are listed once.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Project and source are rows of a Filters menu, the pull request picker's
radio submenu, beside the search box. The MenuSelect dropdown and the agent
panel change that came with it are gone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Name, Project (when listing every project), Source, and Updated columns; a
header sorts by its column and flips on a second press. The dialog is wider
to fit them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Updated sits flush right under its header, and Import / Open are same-size
icon buttons with a tooltip.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rows are one line: the workspace folder moves to a sortable Workspace column
with the branch on hover, and a conversation's first prompt shows on hovering
its title.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A one-line text that ends in an ellipsis and, only when clipped, shows the
whole of it on hover, measured as the tooltip opens rather than observed. The
import dialog's titles and project names use it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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