After reboot (or just opening a fresh kitty window), you want to restore your workspace:
- One kitty tab per tmux session
- Each tmux session named for the task/ticket it was working on
- Each tmux session had opencode running inside it
- You want to resume the right opencode session (conversation history) in each tab
The missing link: opencode does not expose its session ID in any env var or file at runtime.
- No native multi-row/wrapping tab bar — tabs scroll horizontally when full
select_tabaction gives a searchable overlay list (best workaround for many tabs)kitten @ launch --type=tabcan open tabs programmatically from a script- A restore script can loop over tmux sessions and open a kitty tab per session
- tmux sessions survive reboots if using tmux-resurrect + tmux-continuum
- tmux-continuum auto-saves and restores session layout + running commands
- After restore,
tmux attach -t <name>reconnects to the session
What opencode DOES expose at runtime (in child process env):
OPENCODE=1OPENCODE_PID=<pid>OPENCODE_RUN_ID=<uuid>— a UUID, NOT the session ID, no mapping anywhere
What it does NOT expose:
OPENCODE_SESSION_ID— does not exist- No socket file, no port registry, no state file linking run to session
What does exist:
- The log file
~/.local/share/opencode/log/<timestamp>.logcontainsservice=session id=ses_XXXXXshortly after startup - The DB (
~/.local/share/opencode/opencode.db) has the session withtime_created - opencode prints the session ID to the terminal when it closes
- The daily-log protocol writes
~/Notes/YYYY-MM-DD-<task>.mdwith a Resume block (but only when you say "done" — which doesn't always happen)
On "done"/"wrap up", opencode writes:
~/Notes/YYYY-MM-DD-<task>.md
## Resume
cd <dir> && opencode --session ses_XXXXX
This IS the index for restoring sessions — but only populated when sessions end cleanly.
Run oc instead of opencode. The wrapper launches opencode, then polls the log
until ses_XXXXX appears (usually within 1-2 seconds), then stores it in tmux:
#!/bin/bash
# ~/bin/oc
opencode "$@" &
OC_PID=$!
sleep 2 # opencode writes session ID to log within ~1s of startup
logfile=$(ls -t ~/.local/share/opencode/log/*.log | head -1)
ses_id=$(grep -oP 'service=session id=\Kses_\S+' "$logfile" | head -1)
if [ -n "$ses_id" ]; then
tmux set-environment OPENCODE_SESSION "$ses_id"
tmux set-option -w @opencode_session "$ses_id"
fi
wait $OC_PIDPro: Works today, no opencode changes needed
Con: sleep is inelegant; race condition if machine is slow
Same wrapper but poll the DB instead of the log — query for sessions newer than script start time:
START_MS=$(date +%s%3N)
opencode "$@" &
OC_PID=$!
for i in $(seq 1 20); do
sleep 0.5
ses_id=$(sqlite3 ~/.local/share/opencode/opencode.db \
"SELECT id FROM session WHERE parent_id IS NULL AND time_created > $START_MS ORDER BY time_created DESC LIMIT 1;")
[ -n "$ses_id" ] && break
done
[ -n "$ses_id" ] && tmux set-option -w @opencode_session "$ses_id"
wait $OC_PIDPro: DB is authoritative; no log format dependency
Con: Still polling; sqlite3 must be available
Hook into tmux-resurrect's save process. Before saving, for each pane running opencode, query the DB for the most recent session matching that pane's cwd:
# In ~/.tmux/plugins/tmux-resurrect/scripts/save.sh hook or
# ~/.config/tmux/resurrect-hook.sh
tmux list-panes -a -F '#{session_name} #{pane_current_path}' | while read name dir; do
ses=$(sqlite3 ~/.local/share/opencode/opencode.db \
"SELECT id FROM session WHERE directory='$dir' AND parent_id IS NULL
ORDER BY time_updated DESC LIMIT 1;")
[ -n "$ses" ] && tmux set-option -t "$name" @opencode_session "$ses"
donePro: No wrapper needed; works even for sessions you forgot to "done"
Con: Matches by directory, not by which opencode was actually running (ambiguous
if you ran opencode multiple times in same dir)
Write an opencode plugin (TypeScript) that subscribes to session.created bus
events and writes the session ID to a known file or sets a tmux env var:
// ~/.config/opencode/plugins/tmux-session-id.ts
export default {
onSessionCreated(session) {
const { execSync } = require('child_process')
execSync(`tmux set-option -w @opencode_session "${session.id}"`)
// or: fs.writeFileSync('/tmp/opencode-session-id', session.id)
}
}Pro: Clean, event-driven, no polling
Con: Need to understand the plugin API; plugins currently have limited event access
Request that opencode set OPENCODE_SESSION_ID in the environment at startup
(like it already does with OPENCODE_PID). This would make everything trivial:
# From any bash tool call inside opencode:
tmux set-environment OPENCODE_SESSION "$OPENCODE_SESSION_ID"Or the daily-log protocol could do it automatically at session start.
Pro: The right fix
Con: Requires upstream change
Once tmux windows have @opencode_session set, the restore script is straightforward:
#!/bin/bash
# ~/.config/kitty/restore-workspace.sh
# Open a kitty tab per tmux session, resume opencode if session ID is stored
tmux list-sessions -F '#{session_name} #{session_path}' | while read name path; do
ses_id=$(tmux show-option -gqv @opencode_session 2>/dev/null)
# Build the command to run in the tab
if [ -n "$ses_id" ]; then
cmd="cd '$path' && opencode --session '$ses_id'"
else
cmd="cd '$path' && tmux attach -t '$name'"
fi
kitten @ launch --type=tab --tab-title "$name" --cwd "$path" -- bash -c "$cmd"
doneBind in kitty.conf:
map ctrl+shift+r launch --type=background bash ~/.config/kitty/restore-workspace.sh
- Option B (DB polling wrapper) as
~/bin/oc— best balance of reliability and simplicity - Option C (resurrect save hook) as a safety net — catches sessions you didn't start with
oc - Notes protocol already handles clean exits via "done"
- File Option E as a GitHub issue against opencode
- opencode exposes
OPENCODE_SESSION_IDin env → everything simplifies dramatically - Plugin API matures → Option D becomes clean