Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
b495d43
Add detachable SSH PTY daemon persistence
lawrencecchen May 26, 2026
406166f
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 26, 2026
97cdaea
Fix persistent SSH PTY review findings
lawrencecchen May 26, 2026
fc5f4b0
Bound persistent daemon auth handshake
lawrencecchen May 26, 2026
4251df2
Fix persistent SSH daemon review findings
lawrencecchen May 26, 2026
e2387ba
Enforce persistent daemon slot invariant
lawrencecchen May 26, 2026
595cd75
Fix persistent SSH restore control paths
lawrencecchen May 26, 2026
c5af267
Preserve persistent daemon sessions on auth rotation
lawrencecchen May 26, 2026
c3dc301
Fix SSH fork and daemon cleanup regressions
lawrencecchen May 26, 2026
693987b
test: cover persistent SSH PTY relaunch snapshots
lawrencecchen May 27, 2026
aa85f40
fix: persist SSH PTY session IDs through relaunch
lawrencecchen May 27, 2026
bba2b95
merge: sync with main
lawrencecchen May 27, 2026
49e9ccb
fix: preserve workspace id for SSH PTY fallback
lawrencecchen May 27, 2026
441e40b
fix: preserve SSH options in reconnect restore
lawrencecchen May 27, 2026
69714d2
fix: remap restored SSH relay context
lawrencecchen May 27, 2026
81d464a
fix: use unique daemon token temp files
lawrencecchen May 27, 2026
ca45ff5
fix: address ssh pty review findings
lawrencecchen May 27, 2026
1f9ed43
fix: tighten ssh pty review feedback
lawrencecchen May 27, 2026
dc93889
fix: keep relay alias rewrites on relay queue
lawrencecchen May 27, 2026
e7be943
fix: clean stale persistent ssh proxies
lawrencecchen May 27, 2026
f4352ce
fix: scope persistent ssh cleanup by slot
lawrencecchen May 27, 2026
d09db53
fix: gate persistent ssh restore on socket
lawrencecchen May 27, 2026
5e80396
fix: preserve generic ssh cleanup with slots
lawrencecchen May 27, 2026
7c56a18
fix: bound persistent daemon slot lifecycle
lawrencecchen May 27, 2026
7d96b10
fix: parse persistent daemon cleanup slots by token
lawrencecchen May 27, 2026
4547993
fix: simplify restored relay token guards
lawrencecchen May 27, 2026
c867b8f
fix: avoid reattaching ended ssh ptys
lawrencecchen May 27, 2026
c1d9fe5
fix: match escaped ssh daemon slot cleanup
lawrencecchen May 27, 2026
d7fe0a1
fix: harden persistent daemon socket dir
lawrencecchen May 27, 2026
4f7897c
test: cover ssh pty reattach command restore
lawrencecchen May 27, 2026
f94e6fa
fix: execute persisted ssh pty attach scripts
lawrencecchen May 27, 2026
75d5f4b
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 27, 2026
86f7874
fix: keep only persistent ssh pty exits visible
lawrencecchen May 27, 2026
5ea7ba7
fix: harden ssh pty restore fallbacks
lawrencecchen May 27, 2026
64e9205
fix: restore ssh pty pane bootstrap
lawrencecchen May 27, 2026
fb1d7d9
fix: remap restored ssh relay anchors
lawrencecchen May 27, 2026
0d07504
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 27, 2026
082e52e
fix: guard restored ssh shell placeholders
lawrencecchen May 27, 2026
e281dc5
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 28, 2026
e024877
fix: remap restored ssh panel aliases
lawrencecchen May 28, 2026
1736207
fix: cancel stale ssh relay forwards
lawrencecchen May 28, 2026
e1cd86d
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 28, 2026
393d91b
fix: preserve ssh relay aliases across reconnect
lawrencecchen May 28, 2026
b48d88f
fix: keep ssh pty identity on relay refresh
lawrencecchen May 28, 2026
a961514
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 28, 2026
75e5ecb
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 28, 2026
9ea1dac
fix: restore moved ssh pty relay aliases
lawrencecchen May 28, 2026
f374f1f
fix: preserve existing terminfo environment
lawrencecchen May 28, 2026
bf0443d
fix: clear failed persistent ssh attach state
lawrencecchen May 28, 2026
b116e2e
ci: retrigger stuck PR checks
lawrencecchen May 28, 2026
56254dc
fix: reset persistent ssh sessions on relay changes
lawrencecchen May 28, 2026
5bbb77c
test: keep relay refresh on stable port
lawrencecchen May 28, 2026
3cb5af8
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 28, 2026
e5e1186
test: cover stale ssh relay listener cleanup
lawrencecchen May 29, 2026
6f94d5a
fix: clean stale ssh relay listeners
lawrencecchen May 29, 2026
7ca9850
fix: default persistent ssh daemon slot
lawrencecchen May 29, 2026
da48e45
fix: preserve relay command line endings
lawrencecchen May 29, 2026
1636e8a
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 29, 2026
abe618b
fix: harden persistent ssh relay cleanup
lawrencecchen May 29, 2026
4f7ddc3
fix: suppress remote restore scaffold attaches
lawrencecchen May 29, 2026
ef01b12
merge: sync with main
lawrencecchen May 29, 2026
c534be4
test: use workspaceId in SSH PTY snapshot test
lawrencecchen May 29, 2026
9337eea
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 30, 2026
b900350
fix: address remote relay review feedback
lawrencecchen May 30, 2026
f2aaf6b
refactor: split remote shell bootstrap builder
lawrencecchen May 30, 2026
cb4225e
fix: preserve bash login startup precedence
lawrencecchen May 30, 2026
d2a3396
Merge remote-tracking branch 'origin/main' into feat-ssh-detachable-pty
lawrencecchen May 30, 2026
bfcb6a2
fix: address detachable pty review and ci
lawrencecchen May 30, 2026
9f7cce5
test: harden websocket pty close assertion
lawrencecchen May 30, 2026
f1f6029
ci: prefer homebrew zig for macos release build
lawrencecchen May 30, 2026
f5a0c13
ci: build release artifacts on macos 15
lawrencecchen May 30, 2026
f147972
merge: sync detachable pty with main
lawrencecchen May 30, 2026
7874b99
ci: extend lag regression cold-build timeout
lawrencecchen May 30, 2026
afc56f9
merge: sync detachable pty with latest main
lawrencecchen May 30, 2026
2454261
fix: address persistent ssh restore review
lawrencecchen May 30, 2026
b9b5dd5
fix: preserve failed persistent pty split panes
lawrencecchen May 30, 2026
499efaf
merge: sync detachable pty with latest main
lawrencecchen May 30, 2026
23e6ad4
fix: suppress ended persistent pty restore startup
lawrencecchen May 30, 2026
d895db4
test: avoid ready fd reuse in daemon tests
lawrencecchen May 30, 2026
5f7148c
fix: wire browser tab mute context action
lawrencecchen May 30, 2026
3076ff6
fix: preserve persistent attach failures
lawrencecchen May 30, 2026
3a1cba3
fix: preserve local cwd on remote restore
lawrencecchen May 30, 2026
6a83fd3
merge: sync detachable pty with latest main
lawrencecchen May 31, 2026
9e01e2e
fix: reconnect restored remote browser workspaces
lawrencecchen May 31, 2026
84ef321
test: preserve daemon events during rpc calls
lawrencecchen May 31, 2026
603bb76
fix: wait for restored pty foreground auth
lawrencecchen May 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,9 @@ jobs:
runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
# A cold DerivedData cache (any project.pbxproj or Package.resolved change
# mints a new cache key with no restore-keys fallback) forces a full
# cmux build whose Swift codegen alone runs ~18-20 min, so 20 was right at
# the edge and got guillotined mid-build (the post-cache step never ran, so
# the cache stayed cold and every retry timed out the same way). 35 gives a
# cold build enough room to finish and populate the cache. Extension project
# and package graph changes can still spend more than 35 minutes in the
# build step before the lag regression starts.
# cmux build whose Swift codegen alone can run 20+ min. Project/package
# changes from the sidebar extension kit pushed this full build plus the
# CA and lag regressions beyond 35 min before the cache could repopulate.
timeout-minutes: 55
steps:
- name: Checkout
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/tmux-corpus.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: tmux corpus

on:
pull_request:
paths:
- ".github/workflows/tmux-corpus.yml"
- "daemon/remote/**"
workflow_dispatch:
inputs:
fuzztime:
Expand Down Expand Up @@ -41,7 +45,7 @@ jobs:
- name: Run tmux corpus fuzz targets
working-directory: daemon/remote
env:
FUZZTIME: ${{ github.event.inputs.fuzztime || '2m' }}
FUZZTIME: ${{ github.event_name == 'pull_request' && '30s' || github.event.inputs.fuzztime || '2m' }}
run: |
set -euo pipefail
for target in \
Expand Down
12 changes: 10 additions & 2 deletions CLI/cmux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7461,6 +7461,9 @@ struct CMUXCLI {
sshOptions.extraArguments.isEmpty &&
remoteTerminalBootstrapScript?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false &&
deferredRemoteReconnectCommandScript != nil
let persistentDaemonSlot = usesPersistentSSHPTY
? "ssh-\(UUID().uuidString.lowercased())"
: nil
let startupInitialSSHCommand = buildSSHCommandText(
sshOptions,
localCommandScript: combinedLocalCommandScript
Expand Down Expand Up @@ -7612,8 +7615,9 @@ struct CMUXCLI {
if sshOptions.skipDaemonBootstrap {
configureParams["skip_daemon_bootstrap"] = true
}
if usesPersistentSSHPTY {
if let persistentDaemonSlot {
configureParams["preserve_after_terminal_exit"] = true
configureParams["persistent_daemon_slot"] = persistentDaemonSlot
}

cliDebugLog(
Expand Down Expand Up @@ -7682,6 +7686,9 @@ struct CMUXCLI {
if usesPersistentSSHPTY, let workspaceInitialSurfaceId {
payload["ssh_pty_session_id"] = "ssh-\(workspaceId)-\(workspaceInitialSurfaceId)"
}
if let persistentDaemonSlot {
payload["persistent_daemon_slot"] = persistentDaemonSlot
}
logSSHTiming("complete", extra: "workspace=\(String(workspaceId.prefix(8)))")
if jsonOutput {
print(jsonString(formatIDs(payload, mode: idFormat)))
Expand Down Expand Up @@ -9779,14 +9786,15 @@ struct CMUXCLI {
let quotedSessionID = shellQuote(sessionID)
let currentExecutable = shellQuote(resolvedExecutableURL()?.path ?? (args.first ?? "cmux"))
let attachCommand = "\"$cmux_ssh_attach_cli\" --socket \"$CMUX_SOCKET_PATH\" ssh-pty-attach --wait --require-existing --workspace \"$CMUX_WORKSPACE_ID\" --session-id \(quotedSessionID) --attachment-id \"${CMUX_SURFACE_ID:-}\""
return ([
let script = ([
"cmux_ssh_attach_cli=\"${CMUX_BUNDLED_CLI_PATH:-}\"",
"if [ -z \"$cmux_ssh_attach_cli\" ] || [ ! -x \"$cmux_ssh_attach_cli\" ]; then cmux_ssh_attach_cli=\(currentExecutable); fi",
"if [ -z \"$cmux_ssh_attach_cli\" ] || [ ! -x \"$cmux_ssh_attach_cli\" ]; then cmux_ssh_attach_cli=\"$(command -v cmux 2>/dev/null || true)\"; fi",
"if [ -z \"$cmux_ssh_attach_cli\" ]; then printf '%s\\n' '[cmux] bundled CLI not found for SSH PTY attach.' >&2; exit 127; fi",
"if [ -z \"${CMUX_SOCKET_PATH:-}\" ]; then printf '%s\\n' '[cmux] required configuration missing for SSH PTY attach.' >&2; exit 1; fi",
"if [ -z \"${CMUX_WORKSPACE_ID:-}\" ]; then printf '%s\\n' '[cmux] required workspace context missing for SSH PTY attach.' >&2; exit 1; fi",
] + sshPTYAttachRetryLoopLines(command: attachCommand)).joined(separator: "\n")
return "/bin/sh -c \(shellQuote(script))"
}

private func sshPTYAttachRetryLoopLines(command: String) -> [String] {
Expand Down
Loading
Loading