Draft: two-way teleport with native CLI sessions - #7136
Conversation
Contracts, orchestration, and the teleport service land first. Native CLI formats register later, so list/import/export stay empty until a provider adapter is added. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
The UI can list, import, and export native sessions. Until a format adapter is registered, the picker stays empty and export fails closed. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Register the Codex jsonl adapter so T3 can list, import, and export rollouts the CLI can resume. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Register the OpenCode adapter so T3 can list, import, and export text turns from opencode.db or JSON storage without treating the live db as a foreign lock. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Register the Claude jsonl adapter so T3 can list, import, and export sessions from the Claude projects folder. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Register the Grok session-directory adapter so T3 can list, import, and export native Grok Build chats. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Stop marked the turn interrupted but left the session running, so the thread stayed Working after abort hung or only emitted turn.aborted. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
A projected running OpenCode thread stayed Working after Stop if the in-memory provider session was already gone. Interrupt no longer recovers a session just to abort it, and Stop settles the projection when there is nothing live to interrupt. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Keep live-session titles and personal directories out of the public diff. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
|
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:
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.
Two small consistency findings in the new teleport UI. Everything else in the changed web files (command palette views, composer disabled/placeholder handling, ChatView toast) follows the existing primitives and layout conventions.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Effect service conventions review of the new TeleportService (plus its contract errors). Four convention violations found; details inline.
Posted via Macroscope — Effect Service Conventions
The import CTA lacked cursor-pointer, and the header export control used the text xs size instead of the square icon-xs used by neighboring actions. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Reject unsafe session ids on native reads/writes, fail closed when lock checks cannot run, keep Codex whitespace, preserve in-place provider instances, and clear stale history/approvals when replacing a thread. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
There was a problem hiding this comment.
One finding: the new header control's disabled state suppresses the tooltip that explains why it is unavailable.
Posted via Macroscope — UI Consistency
First export no longer reuses the live SDK resume id, so Claude/Codex transcripts are not overwritten. Presence is set before the native write and reverted if that write fails. Worktree threads export from thread.worktreePath. In-place import stops an idle adapter, unarchives an existing binding instead of duplicating it, and keeps user titles. Imported messages get fresh T3 ids, shared-home custom instances still load, oversized session files are skipped, Claude meta/subagent records are ignored, and history replace prunes leftover attachments. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Mobile enabled Send from draft content alone, then discarded the queued message when the server rejected the teleported-out turn. Disable send and skip enqueue so the draft stays put. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
There was a problem hiding this comment.
One consistency finding on the new header control; everything else (palette icon classes, variant="outline" size="icon-xs" matching the sibling header triggers, composer disabled/placeholder wiring) lines up with the existing patterns.
Posted via Macroscope — UI Consistency
The header control stays hoverable so its tooltip remains reachable, but the pointer still looked clickable. Match the other inert header actions. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Review comments on the OpenCode stack: JSON-only sessions disappeared when sqlite had any match, sqlite cwd filtering dropped symlink spellings, client nativePath could escape the instance root, unknown instance ids fell back to the default home, and in-place import could unarchive a busy thread then restore a stale busy binding. Tagged teleport errors now derive message from a structured reason field. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
A supplied session path was accepted when the session cwd was a generic parent such as /tmp. Load now uses the same OpenCode project matcher as listing, so parent project folders still import and generic roots do not. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Keep Codex and Claude teleport. Grok and OpenCode remain T3 providers, but their native CLI import/export is parked: they are far less used and the implementations are jankier than the jsonl writers. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
There was a problem hiding this comment.
Reviewed the new teleport service and its call sites against the Effect service conventions. One finding: the native format adapters are wired through a mutable module-global registry populated by import side effects rather than through an Effect service/layer.
Posted via Macroscope — Effect Service Conventions
Replace the import-time mutable adapter map with TeleportFormatRegistry so lookups do not depend on module load order. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
There was a problem hiding this comment.
One finding: the new teleport lock probe spawns an external command through a module-global execFile instead of acquiring the existing ProcessRunner service, so that dependency is not represented in the Effect environment. Everything else in the new TeleportService / TeleportFormatRegistry modules follows the canonical tag → make → layer shape, acquires its dependencies with yield* Foo.Foo, and uses Effect.catchTags for known tagged failures.
Posted via Macroscope — Effect Service Conventions
lsof and the Windows write-open check now use ProcessRunner and FileSystem so lock probes are injectable instead of module globals. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Stop still marks the session ready immediately so the UI can leave Working, but sendTurn now waits until session.abort finishes so the pending abort cannot cancel the next prompt. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
History replace used the sanitized thread-id segment as ownership, so ids that collapse to the same segment could delete another thread's files. Prune only paths previously referenced by the replaced thread. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Import session choices were built from collapsed picker targets, so sibling environments in a logical group could not be imported from. Build the list from physical projects instead. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
An extra Codex or Claude instance with an explicit empty homePath inherited the default instance's home. Preserve empty strings so those instances use the provider default. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Effect 4 does not provide zipRight on Effect. Complete the sendTurn abort latch from a generator finalizer instead. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Claim the in-flight abort through an atomic gate so a second interrupt waits instead of emitting another turn.aborted or calling session.abort again. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Export writes the native session cwd from the thread worktree, which lives outside the project root. Listing and import treated only the workspace root as in-project, so Teleport Out was a one-way door. Discover extra cwds from the project's thread worktrees, match subdirectory sessions, and stop accepting ancestor cwds such as / or $HOME. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Export set presence to native with a teleport-pending path before writing the CLI file, then only reverted on write tapError. Interruptions, defects, and later directory or teleport.set failures left the thread stuck: the UI only offered Import, which rejected the fake path. Wrap the pending presence in acquireUseRelease so any unsuccessful exit reverts to T3, or persists the real native path if the file was written. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Release-path persist of a written native session can still raise a filesystem error. Catch that so a failed bind cannot mask the export failure or skip reverting pending presence. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
There was a problem hiding this comment.
Two error-modeling convention issues in the new teleport code: message stored as data when it is a fixed template over existing attributes, and a helper whose only behavior is constructing that error.
Posted via Macroscope — Effect Service Conventions
Lock, schema, identity, and file-locked errors now derive `message` from their fields, so the lockProbeError helper can go. A failed new-thread import deletes the orphan thread instead of leaving it unbound. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
There was a problem hiding this comment.
Reviewed the new teleport service against the Effect service conventions. Service shape (Context.Service with inline interface, exported make/layer), dependency acquisition via yield*, and Effect.catchTags usage all look right now. Three remaining error-modelling issues below.
Posted via Macroscope — Effect Service Conventions
Store a write stage instead of a free-form message on TeleportNativeWriteError, derive TeleportUnsupportedProviderError from the provider, and remap schema verification with catchTags. On Windows, replace native files by moving the destination aside so a failed rename can restore the previous session. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
Persist teleport ownership first so a failed history replace cannot leave an unbound thread with overwritten messages. Co-authored-by: aanishbhirud <aanishbhirud@gmail.com>
What Changed
T3 can list, import, and export native CLI sessions for Codex, Claude, Grok, and OpenCode.
Native files stay native. Import copies a CLI session onto a T3 thread. Export writes the thread back. Presence (
t3vsnative) swaps Import/Export so both sides are not writers at once.Codex export writes
session_metathe CLI can resume. OpenCode imports text turns only, does not treat liveopencode.dbas a foreign lock, and does not match parent cwds into siblingcodex/claude/grokfolders. Stop settles Working when the turn is aborted, when there is no live session, or when interrupt fails.Why
Two-way teleport needs a shared engine, UI, and one adapter per CLI. This PR is that whole stack in one diff because stacked bases have to exist on
pingdotgg/t3code.UI Changes
Import lives on the new-thread / sessions view. Export lives in the chat header. Composer send is disabled while the thread is in the native CLI.
Checklist
Note
Add two-way teleport between app threads and native CLI sessions
thread.history.replaceandthread.teleport.setorchestration commands.TeleportServicewith list/import/export session RPCs (teleport.listSessions,teleport.importSessions,teleport.exportSession) wired into the WS layer with scope-based authorization.TeleportOutButtonin the chat header, and composer/send disabling when a thread is teleported-out.teleport_jsontoprojection_threadswith backfill from existing runtime telemetry.thread.turn.startnow fails withOrchestrationCommandInvariantErrorif the thread's teleport presence isnative, blocking turn creation on teleported-out threads.📊 Macroscope summarized 18ed3c0. 52 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.