feat(orchestration): import an existing Claude Code or Codex session by ID - #4617
feat(orchestration): import an existing Claude Code or Codex session by ID#4617Bil0000 wants to merge 13 commits into
Conversation
Transports resolve provider adapters through ProviderService; the adapter registry is not visible past ProviderServiceLive. Reading a provider thread snapshot from an RPC handler needs a facade method, mirroring how rollbackConversation routes.
…d snapshots thread/read already returns the thread's cwd and its recency stamp; the adapter dropped both. Callers that need to know where a codex thread ran, or when it was last active, had nothing to read. Codex reports those stamps in Unix seconds, so parseThreadSnapshot converts them and the field name carries the unit.
…vent Adds the command, the thread.messages-imported event, and the payloads for orchestration.resolveImportSession and orchestration.importThread, plus the decider case that turns one into the other. thread.messages.import stays in the internal command union: only the server dispatches it, and a client must not be able to write a transcript.
…ages Pure mappers from a Claude session transcript or a codex thread snapshot to ThreadImportedMessage. Non-user/assistant entries and empty text are skipped. Message ids are derived from the thread id, provider, transcript index and source message id, so a repeated import cannot duplicate rows. The index is zero-padded because the message projection breaks ties on message id, and an unpadded index would order a ten-message transcript 1, 10, 2.
Applies thread.messages-imported through the existing message projection. No new table, no migration. The command read model folds the same event so the decider's message-based invariants see an imported transcript, matching how every other event that changes thread messages is registered.
Copying a transcript into a thread is not agent activity. Every sibling event that adds message content is already excluded; thread.messages-imported fell through to the default and queued a spurious alert.
…hread resolveImportSession reads where a Claude session ran and whether a project already covers that workspace, so a caller can offer to add the missing project before anything is created. importThread creates the thread in that project, starts the provider session with a resume cursor, copies the transcript in and binds the session. Both drivers refuse a session that ran in another workspace. A failure after the thread exists deletes it, and ThreadDeletionReactor stops the provider session behind that. resolveImportSession takes the orchestration read scope; importThread takes the operate scope.
resolveImportSession and importThread operations plus their atom commands, serialised per session id so the same import cannot run twice concurrently. The thread reducer applies thread.messages-imported so an open thread shows the transcript without waiting for a fresh snapshot.
Opens a dialog that takes one session id, offers Claude Code and Codex, and labels the field Session ID or Thread ID to match. On submit it resolves where the session ran. If a project already covers that workspace the thread lands there; if none does, the dialog says so and the button becomes Add project & import, so nothing is created without a second press.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Effect Service Conventions — 1 finding.
The importFailure helper in apps/server/src/orchestration/importThread.ts is a pure error-construction mapper, which the conventions ask to avoid. See the inline comment.
Posted via Macroscope — Effect Service Conventions
ApprovabilityVerdict: Needs human review This PR introduces a complete new feature for importing Claude Code and Codex sessions, including new RPC endpoints, orchestration events, external SDK integration, and a new UI dialog workflow. New features with this scope of cross-cutting changes and external integrations warrant human review. You can customize Macroscope's approvability policy. Learn more. |
deriveProviderInstanceEntries preserves server ordering, where a configured custom instance can precede the synthesised default. Taking the first ready entry meant the generic Claude Code / Codex option silently resolved through a custom instance and its model configuration.
The global keydown listener toggled the command palette regardless of the import dialog, stacking two modals with competing focus handling.
A project created for the session stayed invisible to the retry path, so pressing Import again built a second project for the same workspace and hit the duplicate-workspace invariant.
…rupted tapError never runs on fiber interruption, so a client disconnecting mid import left the thread in the sidebar and its provider session running. onExit compensates on any non-success exit. Error construction moves to each failure boundary, per the service conventions, so the message and cause stay visible where they originate.
|
All five review findings addressed, one commit each, pushed as
Each was reproduced before being changed. The interruption one has a regression test that forks the import, interrupts it during Typecheck, lint and format clean across |
|
I want to be able to look up stuff from old conversations with a decent UI. |
Happy to hear that man Just didnt get what you mean, can you plz briefly explain exactly what you want? I will make sure to add your request if its smth useful :) |
|
@Bil0000 Apologies, with my previous comment I did not mean to suggest that something about the PR needs to change for my usecase. I was merely venting about how the TUI in cladue code is not good to navigate conversations, Codex or T3-Code are much better at that. If I were to suggest anything to add at all: I would consider it useful, if there was any way
While not strictly necessary, I could see that this would be a nice addition to make t3-code adoption a bit easier |
|
Closing in favor of #2829 (orchestration V2). #2829 deletes the V1 orchestration layer this PR builds on — This is not a judgement on the change itself. Several of these are real gaps we still want fixed; the base just moved out from under them. Once #2829 merges, please rebase onto |
|
@juliusmarminge reimplemented against the V2 orchestrator as #5499, targeting the #2829 branch so it can rebase trivially once that merges. Same UX as this PR plus two-way transcript sync (T3 turns land back in the provider's own session file, and turns made in the CLI after import sync into the thread). Verified live against a real Claude Code session — details in the PR description. |
Why
T3 Code can only continue sessions it started. A Claude Code CLI session or a
codexthread already on the machine has a transcript on disk and is fully resumable, but there is no way to open it here. Starting work in a terminal and wanting to finish it in the web UI means starting over.What
Import session...in the command palette takes one session id.The server reads where that session ran, creates the thread in the project covering that workspace, starts the provider session with a resume cursor, and copies the prior conversation into the transcript. The next turn continues inside the original provider session — the model still has its full context, because nothing is replayed to it.
orchestration.resolveImportSessionreads the session's working directory before anything is created. If a project already covers it, the thread goes there regardless of which project is open.Add project & import. Nothing is created without a second press./statusis surfaced under the field as the fastest way to get a session id.Claude Code and Codex only. No session picker, no
listSessionsbrowsing, no other providers.How it fits together
thread.messages.importcommand and itsthread.messages-importedevent, plus the two RPC payloadsresolveImportSession(read scope) andimportThread(operate scope)resolveImportSession/importThreadoperations and the palette dialogthread.messages.importis in the internal command union: only the server dispatches it, so a client cannot write a transcript.Imported message ids are derived from the thread id, provider, zero-padded transcript index, and source message id — stable ordering, and a repeated import cannot duplicate rows.
A failure after the thread exists deletes that thread, and
ThreadDeletionReactorstops the provider session behind it, so a failed import leaves nothing running and nothing in the sidebar.Supporting changes
Two small provider changes were needed and are split into their own commits:
ProviderServicegainsreadThread. The adapter registry is not visible pastProviderServiceLive, so an RPC handler had no way to read a provider thread snapshot.thread/readalready returned both and the adapter dropped them; without them, codex imports could not get the same workspace check as Claude. Codex reports those stamps in Unix seconds, so they are converted at the boundary and the field name carries the unit.Commits
The branch is nine commits, each of which typechecks on its own:
feat(provider): exposereadThreadon the ProviderService facadefeat(provider): carry codex workspace root and last activity on thread snapshotsfeat(orchestration): add thethread.messages.importcommand and its eventfeat(orchestration): map provider transcripts to imported thread messagesfeat(orchestration): project imported thread messagesfix(relay): do not publish agent awareness for an imported transcriptfeat(orchestration): resume an existing provider session into a new threadfeat(client-runtime): add the session-import operationsfeat(web): add the Import session command palette actionVerification
Focused tests, per
AGENTS.md:Typecheck and format clean across
contracts,client-runtime,server,webandmobile.Manually, against a real Claude Code session:
claude -p "Remember this fact for later: the import canary code is FERRET-4412. Reply with exactly: OK"inside a project directory./status, or from~/.claude/projects/<project>/).Import session...and paste it.Add project & import.Not covered
Codex was not exercised at runtime — no
codexCLI on the machine used. Its transcript mapper, workspace check and timestamp conversion are unit-tested; the live resume path is not.Video
CleanShot.2026-07-27.at.11.24.49.mp4
Note
Add import of existing Claude Code or Codex sessions by ID into new threads
resolveImportSessionandimportThreadRPC endpoints on the WebSocket layer, allowing clients to look up an external session and import its transcript into a new orchestration thread.importThread.tsfactory orchestrates the full flow: validates provider support (claudeAgent/codex), creates a thread, converts the external transcript viaimportedMessages.ts, dispatchesthread.messages.importandthread.session.set, and cleans up viathread.deleteon failure.thread.messages-importedevent type through the full stack: contracts, decider, projector, projection pipeline, and client-side reducer, with deduplication bymessageId.ImportSessionDialogUI component in the web app, accessible via a new "Import session..." action in the command palette, which guides the user through provider selection, session ID input, optional project creation, and navigation to the imported thread.Macroscope summarized db5946c.