fix(remote): address review feedback on omp/claude respawn continuity

- Remote omp command now renders through buildSpawnCommandFromRegistry
  (the mode-agnostic engine local/docker spawns use) instead of the
  buildOmpCommand() the CLI-registry refactor deleted.
- Session._pinOmpRespawnId()/_maybeCaptureOmpSessionId() now skip
  host-local ~/.omp resolution entirely for a remote session and fall
  back to --continue: that resolver only ever reads THIS host's
  filesystem, which is meaningless (and could wrongly alias an
  unrelated local conversation) for a conversation that lives on the
  remote host.
- Remote-claude launch now honors an explicit resumeSessionId distinct
  from sessionId (mirrors claudeDockerPaneCommand's shape), and
  validates sessionId the same way that sibling does before
  interpolating it into the remote shell command.
- Add the still-missing header-cwd half of the trailing-slash test,
  and document respawn/reattach continuation + auto-reconnect-vs-
  clean-exit in docs/remote-sessions.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
timkjr
2026-09-07 22:11:54 -05:00
co-authored by Claude Sonnet 5
parent 88243e9ffa
commit 797f0d387c
6 changed files with 206 additions and 32 deletions
+18
View File
@@ -1775,6 +1775,19 @@ export class Session extends EventEmitter {
// (reported live 2026-08-27, fixed in 13a19f79); this guard keeps that
// fix intact now that resolution has moved out of the eager options build.
if (!this._muxSession) return;
// `resolveAndClaimOmpSessionId` scans THIS HOST's `~/.omp/agent/sessions/`, which is
// meaningless for a remote session — the conversation and its session file live on the
// remote host, under the REMOTE user's home. Worse than a no-op: `this.workingDir` for a
// remote session is the remote path (e.g. `/home/user/dotfiles`), so if the local machine
// happens to have its own omp history under a directory that mangles to the same name,
// this would silently claim and pin a COMPLETELY UNRELATED local session's id onto a
// remote respawn. Skip straight to the CLI's own `--continue` fallback, which the remote
// pane command already renders (see buildRemoteLaunchCommand's omp branch) — safe there
// because each remote respawn talks to exactly one remote pane's own omp history.
if (this._remote) {
this._ompConfig = { ...this._ompConfig, continueSession: true };
return;
}
const resolvedId = resolveAndClaimOmpSessionId(this.workingDir);
if (resolvedId) {
this._ompConfig = { ...this._ompConfig, resumeSessionId: resolvedId };
@@ -2568,6 +2581,11 @@ export class Session extends EventEmitter {
*/
private _maybeCaptureOmpSessionId(): void {
if (getCli(this.mode)?.capabilities.transcript !== 'omp-jsonl' || this._claudeSessionId !== this.id) return;
// Same host-local-filesystem trap as `_pinOmpRespawnId`: the omp session file for a
// remote session lives on the remote host, not here, so scanning locally risks aliasing
// this session onto an unrelated local omp conversation that happens to mangle to the
// same directory name. Never resolvable from here — skip.
if (this._remote) return;
try {
const resolvedId = resolveAndClaimOmpSessionId(this.workingDir);
if (resolvedId) {