feat(tmux-subagent): add replace action to prevent mass eviction

- Add column-based splittable calculation (getColumnCount, getColumnWidth)
- New decision tree: splittable → split, k=1 eviction → close+spawn, else → replace
- Add 'replace' action type using tmux respawn-pane (preserves layout)
- Replace oldest pane in-place instead of closing all panes when unsplittable
- Prevents scenario where all agent panes get closed leaving only 1
This commit is contained in:
justsisyphus
2026-01-26 15:25:05 +09:00
parent 8ebc933118
commit 04f2b513c6
7 changed files with 188 additions and 66 deletions
+12 -5
View File
@@ -166,11 +166,11 @@ export class TmuxSessionManager {
canSpawn: decision.canSpawn,
reason: decision.reason,
actionCount: decision.actions.length,
actions: decision.actions.map((a) =>
a.type === "close"
? { type: "close", paneId: a.paneId }
: { type: "spawn", sessionId: a.sessionId }
),
actions: decision.actions.map((a) => {
if (a.type === "close") return { type: "close", paneId: a.paneId }
if (a.type === "replace") return { type: "replace", paneId: a.paneId, newSessionId: a.newSessionId }
return { type: "spawn", sessionId: a.sessionId }
}),
})
if (!decision.canSpawn) {
@@ -190,6 +190,13 @@ export class TmuxSessionManager {
sessionId: action.sessionId,
})
}
if (action.type === "replace" && actionResult.success) {
this.sessions.delete(action.oldSessionId)
log("[tmux-session-manager] removed replaced session from cache", {
oldSessionId: action.oldSessionId,
newSessionId: action.newSessionId,
})
}
}
if (result.success && result.spawnedPaneId) {