mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 20:49:41 +02:00
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:
co-authored by
Claude Sonnet 5
parent
88243e9ffa
commit
797f0d387c
@@ -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) {
|
||||
|
||||
+22
-7
@@ -778,19 +778,34 @@ export function buildRemoteLaunchCommand(options: {
|
||||
modeCommand = override;
|
||||
} else if (mode === 'claude') {
|
||||
// Deterministic conversation pinning for SSH-remote claude (mirrors the
|
||||
// docker-claude shape in claudeDockerPaneCommand): the FIRST run creates
|
||||
// the conversation under --session-id <sessionId>; a respawn / reattach
|
||||
// re-runs the same idempotent command, --session-id exits non-zero
|
||||
// ("already in use"), and the `||` fallback RESUMES that same
|
||||
// docker-claude shape in claudeDockerPaneCommand, INCLUDING the distinct
|
||||
// resumeId branch it declares — this used to only mirror the same-id
|
||||
// fallback shape, silently dropping an explicit resumeSessionId that
|
||||
// differs from sessionId, e.g. a resume-from-history launch): the FIRST
|
||||
// run creates the conversation under --session-id <sessionId>; a respawn
|
||||
// / reattach re-runs the same idempotent command, --session-id exits
|
||||
// non-zero ("already in use"), and the `||` fallback RESUMES that same
|
||||
// conversation. Without a pinned id, every reattach relaunched a bare
|
||||
// `claude` and started a NEW conversation (found live 2026-08-29: remote
|
||||
// claude ctrl-d / ctrl-c relaunched a fresh session). A per-host
|
||||
// `commands.claude` override stays authoritative (admin's explicit
|
||||
// choice) and skips this entirely.
|
||||
const permFlags = buildClaudePermissionFlags(claudeMode, allowedTools);
|
||||
modeCommand = remoteLoginShellCommand(
|
||||
`claude${permFlags} --session-id ${sessionId} || claude${permFlags} --resume ${sessionId}`
|
||||
);
|
||||
const cmd = `claude${permFlags}`;
|
||||
// Defense in depth, mirroring claudeDockerPaneCommand's own belt-and-braces check:
|
||||
// sessionId is server-minted and always safe in practice, but this command is built
|
||||
// as a single shellescaped string and then executed as shell code on the remote
|
||||
// host, so an unsafe value here is validated rather than trusted.
|
||||
if (!RESUME_ID_SAFE.test(sessionId)) {
|
||||
modeCommand = remoteLoginShellCommand(cmd);
|
||||
} else {
|
||||
const rid = resumeSessionId && RESUME_ID_SAFE.test(resumeSessionId) ? resumeSessionId : undefined;
|
||||
modeCommand = remoteLoginShellCommand(
|
||||
rid && rid !== sessionId
|
||||
? `${cmd} --resume ${rid} || ${cmd} --session-id ${sessionId}`
|
||||
: `${cmd} --session-id ${sessionId} || ${cmd} --resume ${sessionId}`
|
||||
);
|
||||
}
|
||||
} else if (mode === 'omp') {
|
||||
// Remote OMP respawn must RESUME the same conversation instead of
|
||||
// relaunching fresh (found live 2026-08-29: remote ctrl-c/ctrl-d relaunched
|
||||
|
||||
Reference in New Issue
Block a user