Repository navigation
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 <[email protected]>
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 <[email protected]>
Register the Codex jsonl adapter so T3 can list, import, and export rollouts the CLI can resume. Co-authored-by: aanishbhirud <[email protected]>
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 <[email protected]>
Register the Claude jsonl adapter so T3 can list, import, and export sessions from the Claude projects folder. Co-authored-by: aanishbhirud <[email protected]>
Register the Grok session-directory adapter so T3 can list, import, and export native Grok Build chats. Co-authored-by: aanishbhirud <[email protected]>
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 <[email protected]>
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 <[email protected]>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <[email protected]>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <[email protected]>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <[email protected]>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <[email protected]>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <[email protected]>
Keep personal project directories out of the public diff. Co-authored-by: aanishbhirud <[email protected]>
Keep live-session titles and personal directories out of the public diff. Co-authored-by: aanishbhirud <[email protected]>
Co-authored-by: aanishbhirud <[email protected]>
Co-authored-by: aanishbhirud <[email protected]>
Co-authored-by: aanishbhirud <[email protected]>
Co-authored-by: aanishbhirud <[email protected]>
Co-authored-by: aanishbhirud <[email protected]>
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 <[email protected]>
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 <[email protected]>
Macroscope expects new Effect services as a single canonical module with the tag, make, and layer together. Inline dispatch-error construction at the failure boundary instead of a curried helper. Co-authored-by: aanishbhirud <[email protected]>
Older remotes do not implement teleport RPCs. Advertise a teleport capability from current servers and keep the web entry points hidden when it is absent, including the React Native client until it opts in. Co-authored-by: aanishbhirud <[email protected]>
Teleport listed only the default provider home, so instances with a custom homePath never appeared. Scan every configured instance, keep the matching instance id on import, and skip unsafe path reuse. Co-authored-by: aanishbhirud <[email protected]>
…tate Reject history replace and export while a T3 session is live, skip OpenCode message ids that would leave the storage root, and keep the header control hoverable so its idle-state tooltip can show. Co-authored-by: aanishbhirud <[email protected]>
Failed lsof or open checks were reported as a locked native file, so a missing probe looked like a live CLI lock. New Codex and Claude exports also landed under the default home even when the thread used a custom instance such as codex_work. Keep probe failures on their own error tag, allocate files under the bound instance root, and import TeleportService as a namespace at the WS boundary. Co-authored-by: aanishbhirud <[email protected]>
Threads on instances such as codex_work hid Teleport Out because the header treated the instance id as a driver kind. Import could also load the first matching session id across homes. Resolve export support from the instance driver, pass providerInstanceId through the session ref, and drop stale command-palette scans when the import view is left. Co-authored-by: aanishbhirud <[email protected]>
The custom-instance export coverage was appended without removing the original describe, so the capability checks ran twice. Co-authored-by: aanishbhirud <[email protected]>
getThreadShellById and getThreadDetailById already load archived threads, but latest-turn lookup still filtered archived_at IS NULL, so import/UI paths lost turn state. Co-authored-by: aanishbhirud <[email protected]>
An explicit providerInstances envelope replaces legacy providers.codex config. Merging omitted homePath from the legacy field made teleport scan or export to a different home than Codex itself. Co-authored-by: aanishbhirud <[email protected]>
Failed export fell back to a pending t3 sentinel when the directory row was missing, wiping nativePath and nativeRevision so later turns stopped watching the imported file. Co-authored-by: aanishbhirud <[email protected]>
…overy Clearing a leftover importing fence left the provider-directory binding in place, so the next T3 turn could resume the native file without the revision gate. Also treat native+importing directory lag as recoverable. Co-authored-by: aanishbhirud <[email protected]>
Canonicalize omitted providerInstanceId before the duplicate check, load and commit each batch session sequentially, honor thread presence on export, include archived worktree cwds, and delete leftover directory rows when recovery clears an importing fence. Co-authored-by: aanishbhirud <[email protected]>
…eSystem Assigning wrapped.rename failed typecheck because FileSystem.rename is readonly. Spread a new service object instead. Co-authored-by: aanishbhirud <[email protected]>
There was a problem hiding this comment.
One finding: a composer insertion guard was widened to every sendDisabledReason, which blocks ordinary draft input (file-tree mentions, terminal links, type-to-focus) while an image upload is in flight or the thread is still loading. Details inline.
Posted via Macroscope — UI Consistency
A later session that fails to load or unlock could leave earlier sessions already imported while the batch RPC still failed. Validate every ref first so those environmental failures leave zero imports. Co-authored-by: aanishbhirud <[email protected]>
There was a problem hiding this comment.
Reviewed the new apps/server/src/teleport/** service code against the Effect service conventions. Two findings on startup reconciliation error handling. The Effect.catchTag usages in formats/codex.ts and formats/claude.ts flagged in a previous run are still present and unchanged; not re-posting those.
Posted via Macroscope — Effect Service Conventions
There was a problem hiding this comment.
UI Consistency
One new finding on the teleport composer lock. The previously reported insertComposerTextAtEnd gate (apps/web/src/components/chat/ChatComposer.tsx L2803 — isSendDisabled also covers "Image still uploading" / "Sending feedback" / "Messages loading", so file-tree "Add to chat" and mention drops are rejected outside the teleport lock) is still present; not re-commented here.
Posted via Macroscope — UI Consistency
A transient directory.getBinding failure was treated as no binding, so rollback could delete a real provider row. Abort the in-place import instead. Startup recovery now retries once and re-fails interrupts so a shutdown mid-repair cannot look successful. Co-authored-by: aanishbhirud <[email protected]>
File-tree mentions and type-to-focus were blocked by every send-disabled reason. Gate insertTextAtEnd on the teleport lock only, and disable attachment and chip remove controls while that lock is held. Co-authored-by: aanishbhirud <[email protected]>
There was a problem hiding this comment.
One convention finding in the new teleport format registry. Note that the two previously flagged Effect.catchTag usages in apps/server/src/teleport/formats/codex.ts and apps/server/src/teleport/formats/claude.ts are still present; those comments are not repeated here.
Posted via Macroscope — Effect Service Conventions
make only wrapped a synchronous fromAdapters call so the layer could use Layer.effect. Nothing else consumed it. Co-authored-by: aanishbhirud <[email protected]>
The compact button stayed enabled, injected /compact, then submit no-oped on the teleport lock. Disable it and surface the lock reason. Co-authored-by: aanishbhirud <[email protected]>
…very Import recovery can restore presence to native while the directory row is still importing. Finalize was reading the pre-recovery snapshot, so it skipped the repair and left the stores inconsistent. Co-authored-by: aanishbhirud <[email protected]>
An already-open slash/mention menu could still apply /plan, /default, or replacements after the thread teleported out. Guard selection with the same teleport draft lock as other mutations. Co-authored-by: aanishbhirud <[email protected]>
Match the write-path convention by recovering TeleportSchemaVersionError via catchTags in Codex and Claude session listing. Co-authored-by: aanishbhirud <[email protected]>
Reject free-form ancestors such as / so a read-scoped client cannot enumerate native CLI session titles and paths outside T3 projects. Co-authored-by: aanishbhirud <[email protected]>
When a pending export never wrote a native file, flip the thread back to T3 and delete the directory binding so a later export is not rejected as already-native. Co-authored-by: aanishbhirud <[email protected]>
There was a problem hiding this comment.
One finding: apps/server/src/teleport/sessionFile.ts computes the native-session digest with a suppressed node:crypto import instead of acquiring the Effect Crypto service the sibling module already uses. Everything else (new TeleportService tag/make/layer shape, namespace imports, catchTags usage, startup-recovery retry/interrupt handling, and the tagged-error definitions in packages/contracts/src/teleport.ts) matches the conventions.
Posted via Macroscope — Effect Service Conventions
Reuse nativeRevisionFromBytes in readNativeSessionFile so import and observe paths share one SHA-256 implementation instead of node:crypto. Co-authored-by: aanishbhirud <[email protected]>
|
Note 🤖 GPT-5.6 Sol responding on behalf of Theo We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together. Two-way teleport with native CLI sessions combines several separable runtime and lifecycle systems. It needs a smaller product decision and staged implementation before code review will be useful. If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed. |
What this adds
T3 can now list, import, and export native Codex and Claude CLI sessions — "teleport." Start a session in either CLI and pick it up in T3, or hand a T3 thread back to the CLI, without losing the transcript and without two writers ever touching the same session file.
Three ideas hold the design together:
Native files stay native. Import copies a CLI session onto a T3 thread; export writes the thread back out in the CLI's own session format. T3 never becomes the owner of the native file.
One writer at a time. Every thread tracks a presence —
t3ornative— that says which side currently owns the session. The UI swaps Import/Export based on presence, so both sides can never be writers at once.Divergence is detected, never merged. After a successful import, a SHA-256 revision watch tracks the native file. If the CLI file changes or goes missing, further T3 turns are blocked and a conflict banner offers Fork native changes, which copies the diverged transcript into a new T3 thread. Both copies survive; nothing gets clobbered.
Scope note:
TeleportProvideriscodex | claudeAgentonly. Grok and OpenCode remain regular T3 providers — this PR adds no native CLI teleport for them.Fail-closed behavior
Every path that could produce a second writer refuses instead of guessing:
lsof; iflsofcan't be spawned, Unix falls back to an exclusive-open probe and fails closed whenever the file exists. A missing file counts as unlocked, so a first export can still create it.nativeorimporting. The message-loading and attachment-upload disables stack on top of that lock — they don't replace it.thread.turn.start, over WebSocket or HTTP (/api/orchestration/dispatch), requires a matching native revision. The one exception isbootstrap.createThreadwhen the thread doesn't exist yet.Recovery
In-place import is a single orchestration transition (
thread.teleport.import). A failed or interrupted import revertspresence: importingand stays retryable — including first-time directory-bound threads, so a failed first import never looks like it succeeded. Startup recovery deletes leftover new-thread husks and clears or restores existing threads. Custom instance homes, worktree threads, and archived canonical threads are all supported.Projection adds a
teleport_jsoncolumn (migration044) and backfills only validTeleportProvidervalues.Known limitations
There is no cross-store transaction spanning orchestration, the provider directory, and the native file — this is documented, not hidden. Mobile already blocks send while a thread is teleported out; the teleport-in UI is web/desktop only for now.
Why
Work started in the Codex or Claude CLI should be able to continue in T3, and the reverse, without losing the transcript or writing from both sides at once. Presence enforces a single writer, the file lock and revision watch back it up, and forking recovers cleanly when the CLI file changes after import.
Relates to #207, #5146, #5741, and #6590. Supersedes #7136.
UI changes
Import lives on the new-thread / sessions view. Export lives in the chat header. The composer is disabled while a thread is in the native CLI.
Entry points
Tooltips
Composer disabled after teleport out
Native-revision conflict banner
Shown when the revision watch detects the CLI file diverged after import. Send is blocked until the user forks.
Recordings
Codex — full roundtrip: T3 export → native CLI (dark mode) → import back
Claude — full roundtrip: T3 export → native CLI (dark mode) → import back
Checklist
Note
High Risk
Touches turn admission, history replacement, attachment deletion, and native revision gating—mistakes could block sends, lose messages/attachments, or allow divergent native/T3 writers.
Overview
Adds teleport as a first-class thread concern: threads carry
TeleportThreadState(native vs T3 vs importing), new orchestration commands/events (thread.teleport.set,thread.teleport.import,thread.teleport.clear,thread.history.replace), and decider rules that block turns on archived, native, or importing presence while allowing atomic import (unarchive + presence + history replace).Server persistence and projections gain
teleport_json(migration 044 with Codex/Claude backfill), hydrate teleport on shells/details (including archived threads for import/UI), and handlethread.history-replacedby wiping turns, plans, activities, and pending approvals while pruning attachment files via an owned-vs-kept plan so unrelated threads are not deleted.Ingress and runtime: HTTP/WS dispatch runs
TeleportService.requireNativeRevisionForTurnbeforethread.turn.start(skipped for bootstrap create on a missing thread); teleport RPCs get auth scopes; environment advertisescapabilities.teleport. Provider changes treatturn.abortedlike completion for session state and stop recovering stale sessions oninterruptTurn.Mobile mirrors web fail-closed behavior: the thread composer disables send and shows
teleportSendDisabledReasonwhen the thread is teleported out.Reviewed by Cursor Bugbot for commit ca3e9b7. Configure here.
Note
Add two-way teleport with native CLI sessions (Codex/Claude)
TeleportServicethat lists, imports, and exports native CLI sessions (Codex JSONL, Claude JSONL) with format-specific adapters, file locking, bounded reads, atomic writes, and symlink-safe directory traversal.thread.history.replace,thread.teleport.set,thread.teleport.import, andthread.teleport.clear, plus corresponding eventsthread.history-replacedandthread.teleported; the decider blocks turn starts on native/importing presence and can unarchive threads on import.teleport_jsoncolumn onprojection_threads(migration 44) and hydrates it through projection handlers, snapshot queries, and client-side thread reducers.TeleportOutButtonin the chat header, native-conflict banners with fork actions, and composer locking (text, attachments, annotations, review comments) when a thread is teleported out or importing. Mobile disables sending for teleported-out threads.interruptTurninmakeProviderServicenow passesallowRecovery: false, preventing stale-session recovery before an interrupt.thread.turn.startmay now fail early with a native-revision check viaturnStartRequiresNativeRevisionCheckandTeleportService.requireNativeRevisionForTurnbefore dispatch in both HTTP and WS paths; archived threads are returned by by-id snapshot queries (list queries still exclude them).Macroscope summarized a6b7b47.