Sync CLI and app-server sessions

Open 💬 6 comments Opened Mar 14, 2026 by kendonB
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What variant of Codex are you using?

codex-cli 1.16

What feature would you like to see?

If I use codex resume to connect to an open session (e. g. ssh'ing from another device) I'd expect the printed content in the original session to stay up to date, behaving as if it is being remotely controlled.

Likewise I'd expect this syncing between codex app and any third party app server systems.

Additional information

_No response_

View original on GitHub ↗

6 Comments

github-actions[bot] contributor · 4 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #14721
  • #14164
  • #13543

Powered by Codex Action

kendonB · 4 months ago

Duplicates are not duplicates. This issue is more narrow. Two sessions on the same machine connecting to the same uuid chat should stay in sync.

Companionh · 4 months ago

Howdy, I had the same question as you as to why the codex sessions arent synced across devices natively so I made my own workaround to implement the feature.

Its currently in experimental alpha and it requires a self-hosted linux server to share the sessions but the codex session sync is functional.

https://github.com/Companionh/codex-workspace-sync

nylla8444 · 2 months ago

just noticed this.

I thought codex CLI and Codex App session is pointing at the same session ID, it isnt?
after shutting down my CLI session and transferred to my Codex App, I cant see my latest session?
Do I need to do something first or what?

safal207 · 26 days ago

This proposal maps closely to LS work on durable trajectories, continuation identity, and deterministic event replay.

A useful distinction may be:

same thread history
≠ clients are live-synchronized
≠ clients have processed the same event cursor
≠ clients may independently execute the same request

A shared Codex thread could be modeled as one append-only event stream with multiple client projections.

durable thread event log
→ CLI projection
→ desktop projection
→ app-server projection
→ third-party client projection

Each client would track its own delivery cursor:

{
"schema_version": "thread-client-cursor/v0.1",
"thread_id": "thread-123",
"trajectory_id": "project-alpha",
"client": {
"client_id": "ssh-cli-02",
"client_type": "CLI",
"connection_id": "connection-771"
},
"last_received_sequence": 184,
"last_rendered_sequence": 183,
"last_acknowledged_sequence": 183
}

The central invariant would be:

«All attached clients observe the same authoritative thread events, while only one accepted operation creates each turn or side effect.»

This separates synchronization from authority.

For example, an external client may submit:

{
"request_id": "mobile-client:991",
"thread_id": "thread-123",
"operation": "START_TURN",
"expected_thread_sequence": 184,
"payload": {
"text": "Run the remaining integration tests"
},
"deduplication_key": "mobile-client:991"
}

The server would append one authoritative event:

{
"sequence": 185,
"event_id": "event-185",
"event_type": "TURN_STARTED",
"request_id": "mobile-client:991",
"actor": {
"client_id": "mobile-client",
"user_id": "local-operator"
},
"turn_id": "turn-044"
}

Every connected surface would then render that same event.

A possible event set:

  • "USER_MESSAGE_APPENDED"
  • "TURN_STARTED"
  • "ASSISTANT_DELTA"
  • "PLAN_UPDATED"
  • "TOOL_CALL_STARTED"
  • "APPROVAL_REQUESTED"
  • "APPROVAL_RESOLVED"
  • "TURN_COMPLETED"
  • "TURN_INTERRUPTED"
  • "CLIENT_ATTACHED"
  • "CLIENT_DETACHED"

The UI should also preserve actor attribution:

[SSH CLI]
Run the remaining integration tests

rather than making externally submitted input indistinguishable from local keyboard input.

A useful recovery flow could be:

client reconnects
→ supplies last acknowledged sequence
→ server replays missing events
→ client rebuilds projection
→ switches to live subscription

This avoids relying on terminal scraping or full-history polling.

Conflicting operations also need deterministic handling. For example:

client A starts turn at sequence 184
client B also starts turn expecting sequence 184

One request could be accepted, while the second receives:

{
"status": "CONFLICT",
"reason": "THREAD_SEQUENCE_ADVANCED",
"current_sequence": 185,
"retry_policy": "REFRESH_AND_REEVALUATE"
}

This prevents two clients from unknowingly starting duplicate work.

The current policy state should remain server-authoritative:

shared conversation
≠ shared implicit approval authority

A remote or resumed client may observe an approval request, but resolving it should require a separately authenticated capability.

A deterministic conformance fixture could test:

  1. two clients attached to one thread receive the same ordered events;
  2. external user message appears immediately in the original CLI;
  3. assistant streaming deltas appear on every live client;
  4. disconnected client replays only missing events;
  5. duplicate request ID creates one turn;
  6. concurrent stale requests produce a deterministic conflict;
  7. actor identity is visible on every client;
  8. one client interruption updates all projections;
  9. approval observation does not imply approval authority;
  10. process restart preserves thread sequence and client recovery.

Related LS work:

  • recovered-evidence continuation envelope:

https://github.com/safal207/LS/pull/651

  • causal audit:

https://github.com/safal207/LS/issues/594

  • evidence and authorization gates:

https://github.com/safal207/LS/issues/595

  • deterministic replay:

https://github.com/safal207/LS/issues/597

Would a small vendor-neutral "ThreadEvent", "ClientCursor", and concurrent-operation fixture help make multi-client synchronization, replay, attribution, and duplicate-turn prevention machine-testable?

kendonB · 26 days ago

@safal207 these slop posts are unlikely to be helpful