Problem
I use tmux + OpenCode heavily, often with multiple OpenCode panes in the same repository. opencode --continue is not pane-aware, so tmux-resurrect cannot safely restore each pane to the exact OpenCode session it had before.
Example failure:
Tmux session: XYZ
Window 1, pane 1 -> OpenCode session A
Window 1, pane 2 -> OpenCode session B
If B was touched most recently, restoring both panes with `opencode --continue` can reopen B in both panes.
The desired behavior is:
XYZ:1.1 -> session A
XYZ:1.2 -> session B
After tmux-resurrect:
XYZ:1.1 opens session A exactly
XYZ:1.2 opens session B exactly
Source check before implementation
Verified against current source/config before creating this issue:
- Dotfiles
.tmux.conf already uses tmux-resurrect, tmux-continuum, and @continuum-restore 'on'.
- Dotfiles
.tmux.conf has base-index 1, pane-base-index 1, and renumber-windows on, so tests and keys should use the actual tmux coordinate values, not assume zero-based indexes.
- Dotfiles
.config/opencode/tui.json currently only has keybindings and no TUI plugin registration.
- OpenCode current CLI supports
--continue / -c and --session / -s.
- OpenCode current TUI implementation handles
--continue by sorting synced sessions by time.updated descending and selecting the newest root session where parentID === undefined. This is explicitly not pane-aware.
- OpenCode current
--session path navigates directly to the supplied sessionID, which is the primitive we need for exact restore.
- OpenCode current session picker switches sessions by directly navigating to
{ type: "session", sessionID: option.value }, so any solution that only observes CLI startup will miss in-TUI session switches.
- OpenCode current TUI plugin API supports file/path plugins in
tui.json, exposes api.route.current, and for session routes exposes { name: "session", params: { sessionID } }.
- tmux-resurrect saves pane rows with session name, window index, pane index, pane current path, current command, and full command.
- tmux-resurrect restores pane processes by targeting
${session_name}:${window_number}.${pane_index} and supports @resurrect-processes with fuzzy process matching and -> custom restore commands.
Useful source links:
Proposed architecture
Keep tmux-resurrect dumb. It should only restore one generic command for OpenCode panes.
tmux-resurrect restores:
opencode-continue-pane
opencode-continue-pane:
identifies the current tmux session/window/pane/cwd
looks up the exact OpenCode session bound to that pane identity
launches `opencode --session <sessionID>`
The persistent binding should be written while OpenCode is running:
OpenCode TUI plugin:
observe current TUI route
when route is a session route, read exact sessionID
ask tmux for current pane coordinate
write binding:
tmux_session_name + window_index + pane_index + cwd/project_path
-> OpenCode sessionID
Recommended v1 pane identity:
<tmux-session-name>:<window-index>.<pane-index>|<cwd>
Example:
XYZ:1.2|/home/elhaam/projects/peasy -> ses_abc123
Do not use raw tmux %pane_id as the permanent database key. %pane_id is a runtime object ID and should not be assumed to survive a tmux server restart. It is fine to use $TMUX_PANE at runtime only to ask tmux for the stable-ish coordinate.
SQLite is fine for the binding store, but a small file store is also acceptable if it is atomic and easy to inspect. Prefer something debuggable over something clever.
Minimal tmux-side change
The only tmux-resurrect behavior we should need is process restoration through one generic command, roughly:
set -g @resurrect-processes '"~opencode->opencode-continue-pane"'
Exact command/name can change during implementation, but the important constraint is: do not deeply patch tmux-resurrect or generate per-pane restore commands.
Fallback behavior
opencode-continue-pane should behave conservatively:
- If an exact binding exists for current tmux session/window/pane/cwd, open it with
opencode --session <sessionID>.
- If no exact binding exists, either start normal
opencode or opencode --continue.
- Optional flag:
opencode-continue-pane --pick should show an FZF picker for sessions in the current cwd/project, then persist the chosen binding for this pane.
The FZF picker is a manual repair hatch, not the primary restore mechanism.
Correct real-life tests before implementation
These tests are intentionally integration-first. The bug is about tmux/OpenCode behavior, so unit tests alone are not enough.
Test 1: prove opencode --continue is the wrong primitive
Setup:
- Create a tmux session named
oc-restore-test in one repo.
- Create one window with two panes in the same cwd.
- Pane 1 opens OpenCode session A.
- Pane 2 opens OpenCode session B.
- Touch/interact with session B last so it becomes the newest updated session.
- In both panes, run
opencode --continue manually.
Expected current failure:
Both panes prefer the newest root session, not the pane-specific session.
Why this is the correct baseline test:
OpenCode source currently implements --continue by sorting sessions by time.updated and selecting the newest root session. Therefore this test should fail without pane binding, and it proves the feature is necessary.
Test 2: plugin writer updates when switching sessions inside the TUI
Setup:
- Start OpenCode in tmux pane
oc-restore-test:1.1.
- Confirm the binding store records
oc-restore-test:1.1|cwd -> session A.
- Inside the same OpenCode TUI, use the session picker /
/sessions to switch to session B.
- Inspect the binding store again.
Expected:
oc-restore-test:1.1|cwd now maps to session B, not session A.
Why this is the correct test:
OpenCode's session picker directly navigates the TUI route to the selected sessionID. A solution that only hooks CLI startup or process args will pass simple startup tests but fail this real workflow.
Test 3: two panes in same repo restore to different exact sessions
Setup:
oc-restore-test:1.1 has session A.
oc-restore-test:1.2 has session B.
- Both panes are in the same cwd.
- Save tmux-resurrect state.
- Kill the tmux server completely.
- Restore with tmux-resurrect.
Expected:
Pane 1 reopens session A exactly.
Pane 2 reopens session B exactly.
This is the money test. If this fails, the feature does not work.
Test 4: in-TUI switch before save restores the last active session for that pane
Setup:
- Pane
oc-restore-test:1.1 starts with session A.
- Inside OpenCode, switch to session C using the session picker.
- Save tmux-resurrect.
- Kill tmux server.
- Restore.
Expected:
Pane oc-restore-test:1.1 opens session C, not session A.
Why this is correct:
The binding must track the session currently displayed in the pane, not the session OpenCode originally launched with.
Test 5: stale binding protection by cwd/project path
Setup:
- Create binding for
oc-restore-test:1.1|/repo-a -> session A.
- Later create/reuse a tmux session with the same name and pane coordinate but in
/repo-b.
- Run
opencode-continue-pane.
Expected:
It must not open session A from /repo-a.
It should fall back or ask via picker.
Why this is correct:
Tmux session names and coordinates are reusable. CWD/project path must be part of the identity or validation layer.
Test 6: raw tmux pane id is not required after restore
Setup:
- Record
$TMUX_PANE and the coordinate before killing tmux.
- Save and restore from scratch.
- Record
$TMUX_PANE and coordinate after restore.
Expected:
The feature should work even if `$TMUX_PANE` changed.
The lookup should be based on session/window/pane/cwd, not the old `%pane_id`.
Why this is correct:
Tmux-resurrect reconstructs panes by layout/coordinates and sends restore commands to ${session}:${window}.${pane}. It does not require preserving the old runtime %pane_id.
Test 7: manual picker repair flow
Setup:
- Delete or corrupt the binding for a pane.
- Run
opencode-continue-pane --pick.
- Select a known session from the current project.
- Exit and run
opencode-continue-pane again without --pick.
Expected:
The selected session opens and becomes the stored binding for that pane.
The next non-picker run opens the same selected session automatically.
Why this is correct:
This proves the fallback can repair bad/stale mappings without editing SQLite/files manually.
Acceptance criteria
Non-goals
- Do not modify tmux-resurrect internals.
- Do not rely on terminal title, because OpenCode titles are human-readable/truncated and not stable session IDs.
- Do not use
opencode --continue as the exact restore mechanism.
- Do not make this depend on a single repo name or hardcoded project path.
Problem
I use tmux + OpenCode heavily, often with multiple OpenCode panes in the same repository.
opencode --continueis not pane-aware, so tmux-resurrect cannot safely restore each pane to the exact OpenCode session it had before.Example failure:
The desired behavior is:
Source check before implementation
Verified against current source/config before creating this issue:
.tmux.confalready usestmux-resurrect,tmux-continuum, and@continuum-restore 'on'..tmux.confhasbase-index 1,pane-base-index 1, andrenumber-windows on, so tests and keys should use the actual tmux coordinate values, not assume zero-based indexes..config/opencode/tui.jsoncurrently only has keybindings and no TUI plugin registration.--continue/-cand--session/-s.--continueby sorting synced sessions bytime.updateddescending and selecting the newest root session whereparentID === undefined. This is explicitly not pane-aware.--sessionpath navigates directly to the suppliedsessionID, which is the primitive we need for exact restore.{ type: "session", sessionID: option.value }, so any solution that only observes CLI startup will miss in-TUI session switches.tui.json, exposesapi.route.current, and for session routes exposes{ name: "session", params: { sessionID } }.${session_name}:${window_number}.${pane_index}and supports@resurrect-processeswith fuzzy process matching and->custom restore commands.Useful source links:
Proposed architecture
Keep tmux-resurrect dumb. It should only restore one generic command for OpenCode panes.
The persistent binding should be written while OpenCode is running:
Recommended v1 pane identity:
Example:
Do not use raw tmux
%pane_idas the permanent database key.%pane_idis a runtime object ID and should not be assumed to survive a tmux server restart. It is fine to use$TMUX_PANEat runtime only to ask tmux for the stable-ish coordinate.SQLite is fine for the binding store, but a small file store is also acceptable if it is atomic and easy to inspect. Prefer something debuggable over something clever.
Minimal tmux-side change
The only tmux-resurrect behavior we should need is process restoration through one generic command, roughly:
Exact command/name can change during implementation, but the important constraint is: do not deeply patch tmux-resurrect or generate per-pane restore commands.
Fallback behavior
opencode-continue-paneshould behave conservatively:opencode --session <sessionID>.opencodeoropencode --continue.opencode-continue-pane --pickshould show an FZF picker for sessions in the current cwd/project, then persist the chosen binding for this pane.The FZF picker is a manual repair hatch, not the primary restore mechanism.
Correct real-life tests before implementation
These tests are intentionally integration-first. The bug is about tmux/OpenCode behavior, so unit tests alone are not enough.
Test 1: prove
opencode --continueis the wrong primitiveSetup:
oc-restore-testin one repo.opencode --continuemanually.Expected current failure:
Why this is the correct baseline test:
OpenCode source currently implements
--continueby sorting sessions bytime.updatedand selecting the newest root session. Therefore this test should fail without pane binding, and it proves the feature is necessary.Test 2: plugin writer updates when switching sessions inside the TUI
Setup:
oc-restore-test:1.1.oc-restore-test:1.1|cwd -> session A./sessionsto switch to session B.Expected:
Why this is the correct test:
OpenCode's session picker directly navigates the TUI route to the selected
sessionID. A solution that only hooks CLI startup or process args will pass simple startup tests but fail this real workflow.Test 3: two panes in same repo restore to different exact sessions
Setup:
oc-restore-test:1.1has session A.oc-restore-test:1.2has session B.Expected:
This is the money test. If this fails, the feature does not work.
Test 4: in-TUI switch before save restores the last active session for that pane
Setup:
oc-restore-test:1.1starts with session A.Expected:
Why this is correct:
The binding must track the session currently displayed in the pane, not the session OpenCode originally launched with.
Test 5: stale binding protection by cwd/project path
Setup:
oc-restore-test:1.1|/repo-a -> session A./repo-b.opencode-continue-pane.Expected:
Why this is correct:
Tmux session names and coordinates are reusable. CWD/project path must be part of the identity or validation layer.
Test 6: raw tmux pane id is not required after restore
Setup:
$TMUX_PANEand the coordinate before killing tmux.$TMUX_PANEand coordinate after restore.Expected:
Why this is correct:
Tmux-resurrect reconstructs panes by layout/coordinates and sends restore commands to
${session}:${window}.${pane}. It does not require preserving the old runtime%pane_id.Test 7: manual picker repair flow
Setup:
opencode-continue-pane --pick.opencode-continue-paneagain without--pick.Expected:
Why this is correct:
This proves the fallback can repair bad/stale mappings without editing SQLite/files manually.
Acceptance criteria
opencode-continue-paneexists and can be used as the one generic restore command for OpenCode panes.tui.jsonand records active session changes.%pane_id.--pickmode can repair/create a binding interactively.Non-goals
opencode --continueas the exact restore mechanism.