Skip to content

Draft: two-way teleport with native CLI sessions - #7136

Draft
baanish wants to merge 52 commits into
pingdotgg:mainfrom
baanish:cursor/teleport-opencode-1e23
Draft

Draft: two-way teleport with native CLI sessions#7136
baanish wants to merge 52 commits into
pingdotgg:mainfrom
baanish:cursor/teleport-opencode-1e23

Conversation

@baanish

@baanish baanish commented Aug 15, 2026

Copy link
Copy Markdown

Draft. Do not merge. Exploration of two-way teleport. Opened as one PR because GitHub cannot stack cross-fork bases on this repo.

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 (t3 vs native) swaps Import/Export so both sides are not writers at once.
Codex export writes session_meta the CLI can resume. OpenCode imports text turns only, does not treat live opencode.db as a foreign lock, and does not match parent cwds into sibling codex / claude / grok folders. 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

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Add two-way teleport between app threads and native CLI sessions

  • Introduces a full teleport system allowing threads to be exported to native CLI sessions (Claude, Codex, Grok, OpenCode) and imported back, with message history synchronization via new thread.history.replace and thread.teleport.set orchestration commands.
  • Adds TeleportService with list/import/export session RPCs (teleport.listSessions, teleport.importSessions, teleport.exportSession) wired into the WS layer with scope-based authorization.
  • Adds per-provider JSONL/directory format adapters with schema-version gating, file-lock detection, and atomic writes; a registry pattern enables runtime format registration.
  • Extends the command palette and chat UI with session browsing, import flows, a TeleportOutButton in the chat header, and composer/send disabling when a thread is teleported-out.
  • Adds DB migration 041 adding teleport_json to projection_threads with backfill from existing runtime telemetry.
  • Risk: thread.turn.start now fails with OrchestrationCommandInvariantError if the thread's teleport presence is native, 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.

cursoragent and others added 20 commits August 15, 2026 08:54
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>
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0209abea-ab9a-4e89-8112-0400ee85f9af

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 15, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/web/src/components/chat/DraftHeroHeadline.tsx Outdated
Comment thread apps/web/src/components/teleport/TeleportOutButton.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect service conventions review of the new TeleportService (plus its contract errors). Four convention violations found; details inline.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/teleport/Services/TeleportService.ts Outdated
Comment thread apps/server/src/teleport/Layers/TeleportService.ts Outdated
Comment thread packages/contracts/src/teleport.ts Outdated
Comment thread apps/server/src/teleport/Layers/TeleportService.ts Outdated
Comment thread apps/server/src/teleport/Layers/TeleportService.ts Outdated
Comment thread apps/server/src/orchestration/Layers/ProjectionPipeline.ts
Comment thread apps/server/src/teleport/formats/codex.ts
Comment thread apps/server/src/teleport/formats/opencode.ts Outdated
Comment thread apps/server/src/teleport/formats/opencode.ts Outdated
Comment thread apps/server/src/teleport/cwd.ts Outdated
Comment thread apps/server/src/teleport/formats/grok.ts Outdated
Comment thread apps/server/src/teleport/TeleportService.ts Outdated
Comment thread apps/server/src/teleport/Layers/TeleportService.ts Outdated
Comment thread apps/server/src/teleport/fileLock.ts Outdated
Comment thread apps/server/src/orchestration/decider.ts Outdated
baanish and others added 3 commits August 15, 2026 15:30
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>

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new header control's disabled state suppresses the tooltip that explains why it is unavailable.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/teleport/TeleportOutButton.tsx Outdated
cursoragent and others added 2 commits August 16, 2026 07:46
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>
Comment thread apps/server/src/teleport/cwd.ts Outdated
Comment thread apps/server/src/teleport/TeleportService.ts Outdated
Comment thread apps/server/src/teleport/TeleportService.ts Outdated
Comment thread apps/server/src/teleport/formats/opencode.ts Outdated
Comment thread apps/server/src/teleport/discovery.ts Outdated
Comment thread apps/server/src/orchestration/Layers/ProjectionPipeline.ts Outdated
Comment thread apps/server/src/teleport/discovery.ts Outdated
Comment thread apps/server/src/teleport/discovery.ts

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/web/src/components/teleport/TeleportOutButton.tsx Outdated
cursoragent and others added 2 commits August 17, 2026 01:52
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>
Comment thread apps/server/src/teleport/discovery.ts Outdated
cursoragent and others added 2 commits August 17, 2026 02:02
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>
Comment thread apps/web/src/components/CommandPalette.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/server/src/teleport/formats/registry.ts Outdated
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>

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 → makelayer shape, acquires its dependencies with yield* Foo.Foo, and uses Effect.catchTags for known tagged failures.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/teleport/fileLock.ts Outdated
cursoragent and others added 6 commits August 17, 2026 20:02
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>
Comment thread apps/server/src/provider/Layers/OpenCodeAdapter.ts Outdated
cursoragent and others added 4 commits August 17, 2026 20:35
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>

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/server/src/teleport/fileLock.ts Outdated
Comment thread packages/contracts/src/teleport.ts Outdated
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>
Comment thread apps/server/src/teleport/nativeWrite.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread packages/contracts/src/teleport.ts Outdated
Comment thread packages/contracts/src/teleport.ts Outdated
Comment thread apps/server/src/teleport/formats/codex.ts Outdated
cursoragent and others added 2 commits August 17, 2026 22:33
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants