Merge pull request #4066 from PeterPonyu/fix/2887-filter-terminal-probe-replies

fix(tmux-subagent): drain terminal probe replies during delegated pane startup (#2887)
This commit is contained in:
YeonGyu-Kim
2026-05-21 00:41:57 +09:00
committed by GitHub
2 changed files with 27 additions and 2 deletions
@@ -255,6 +255,30 @@ describe("team-layout-tmux", () => {
expect(commands.some((args) => args[0] === "set-option")).toBe(false)
})
test("#given delegated pane startup #when split-window is called #then -d flag is always present to prevent terminal probe replies leaking into caller pane (fix #2887)", async () => {
// given
const { createTeamLayout } = await loadLayoutModule()
const members = [
{ name: "m1", sessionId: "s-m1", worktreePath: "/tmp/m1" },
{ name: "m2", sessionId: "s-m2", worktreePath: "/tmp/m2" },
{ name: "m3", sessionId: "s-m3", worktreePath: "/tmp/m3" },
]
// when
await createTeamLayout("run-probe-drain", members, tmuxMgr as never)
// then: every split-window call must carry -d so focus never bounces to the
// new pane; without -d, terminal capability probe replies (DA1/DA2, OSC color)
// emitted during pane startup are misrouted to the caller pane's stdin and
// appear as literal garbage text in the main OpenCode chat input.
const commands = getCommands()
const splitWindowCalls = commands.filter((args) => args[0] === "split-window")
expect(splitWindowCalls.length).toBeGreaterThan(0)
for (const splitArgs of splitWindowCalls) {
expect(splitArgs).toContain("-d")
}
})
test("#given ownedSession=false, focusWindowId=@10, gridWindowId=@11 #when removeTeamLayout runs #then tmux kill-window is called twice with -t @10 and -t @11 and kill-session is NEVER called", async () => {
// given
const { removeTeamLayout } = await loadLayoutModule()
@@ -392,7 +416,7 @@ describe("team-layout-tmux", () => {
const commands = getCommands()
const splitCalls = commands.filter((args) => args[0] === "split-window")
expect(splitCalls).toEqual([
["split-window", "-t", process.env.TMUX_PANE ?? "", "-h", "-l", "70%", "-P", "-F", "#{pane_id}", "-c", "/tmp/m1"],
["split-window", "-t", process.env.TMUX_PANE ?? "", "-h", "-d", "-l", "70%", "-P", "-F", "#{pane_id}", "-c", "/tmp/m1"],
])
expect(commands.filter((args) => args[0] === "new-window").length).toBe(0)
})
@@ -61,7 +61,7 @@ function selectExistingTeammatePane(teammatePanes: Array<string>, callerPaneId:
function buildSplitArgs(callerPaneId: string, teammatePanes: Array<string>, member: TeamLayoutMember): Array<string> {
if (teammatePanes.length === 0) {
return ["split-window", "-t", callerPaneId, "-h", "-l", "70%", "-P", "-F", "#{pane_id}", "-c", getPaneWorkingDirectory(member)]
return ["split-window", "-t", callerPaneId, "-h", "-d", "-l", "70%", "-P", "-F", "#{pane_id}", "-c", getPaneWorkingDirectory(member)]
}
return [
@@ -69,6 +69,7 @@ function buildSplitArgs(callerPaneId: string, teammatePanes: Array<string>, memb
"-t",
selectExistingTeammatePane(teammatePanes, callerPaneId),
teammatePanes.length % 2 === 1 ? "-v" : "-h",
"-d",
"-P",
"-F",
"#{pane_id}",