Skip to content

fix(terminal): restore Codex cards to their own conversations - #80

Merged
howdeploy merged 1 commit into
howdeploy:mainfrom
teo-nex:feat/exact-codex-session-restore-20260926
Sep 27, 2026
Merged

howdeploy merged 1 commit into
howdeploy:mainfrom
teo-nex:feat/exact-codex-session-restore-20260926

Conversation

@teo-nex

@teo-nex teo-nex commented Sep 26, 2026

Copy link
Copy Markdown
Contributor

CanvasTTY currently restores every saved Codex card with codex resume --last. When two cards share a working directory, both can reopen the same conversation instead of their own.

Capture Codex's session_id from authenticated lifecycle hooks, persist a validated ID with each saved card, and restore with codex resume <session-id>. Existing saved cards without an ID open Codex's resume picker, avoiding an arbitrary conversation. Explicitly restarting a completed card clears its old ID. The existing "Save terminal sessions" setting remains opt-in.

This covers Codex provider cards launched by CanvasTTY. A plain shell terminal in which a user manually runs codex resume is outside this change.

Validation: npm test (886/886), npm run typecheck, npm run build, and git diff --check passed. The running CanvasTTY app was not restarted; live UI validation is still pending.

@BIackFIame

Copy link
Copy Markdown
Contributor

@teo-nex, привет! Хочу предупредить: мой #82 (одна модель «после перезапуска» для сессий агентов) построен поверх твоего #80, поэтому GitHub показывает там и твой коммит.

Твой механизм для Codex я взял как есть: захват session_id из хуков, id сохраняется с карточкой, восстановление через codex resume <id>, окно выбора, если id нет, и сброс id при обычном Restart. Поверх него в #82:

  • то же точное продолжение разговора для Claude (--resume) и OpenCode (--session);
  • три состояния настройки: не сохранять / открыть окна заново / продолжить разговоры;
  • место в записи сессии под окружения плагинов.

Поле codexThreadId я переименовал в общее threadId. Старые записи с codexThreadId читаются как раньше, только для Codex.

Несколько мест, где наши подходы расходятся. Предлагаю обсудить, как лучше свести:

  1. Окно выбора разговора при восстановлении без id останавливает оркестраторы и субагентов: им некому ответить. В feat(sessions): restore agent sessions through one "after restart" model #82 такая карточка возвращается остановленной с причиной.
  2. Без включённых статус-хуков id не захватывается.
  3. В fix(terminal): restore Codex cards to their own conversations #80 хранилище остаётся v1 и отбросит файл v2 из feat(sessions): restore agent sessions through one "after restart" model #82, если откатиться на версию только с fix(terminal): restore Codex cards to their own conversations #80.
  4. Для завершившейся карточки нет «Продолжить».
  5. Некорректный id сейчас выбрасывает ошибку, а не открывает окно выбора.

Если #80 примут первым, я переставлю #82 на main. Если удобнее перенести что-то из этого в #80, тоже без проблем.

howdeploy pushed a commit that referenced this pull request Sep 27, 2026
Builds on #80 (teo-nex, "restore each Codex card to its own
conversation"): its capture of the conversation id from authenticated
lifecycle hooks, the validated id saved per card, `codex resume <id>`,
the resume picker when no id is known and a plain restart forgetting
the id are kept as they are. This extends the same exact resume to
Claude Code (`claude --resume <id>`) and OpenCode (`opencode --session
<id>`); the field is renamed from codexThreadId to threadId for that,
with one per-provider check (canonical UUID for Codex and Claude, `ses_`
id for OpenCode) shared by the hook client, the gateway, the store and
the launch, and v1 records' codexThreadId still read.

Settings → General now offers Don't save / Reopen windows / Continue
conversations (settings v21; the old opt-in boolean migrates
true→continue, false→off). Session records move to v2 (v1 stays
readable): last state at quit or exit, the thread id, a per-card restore
flag, and two validated opaque plugin slots (launch options and an
environment ref, 4 KB each). No scrollback, prompts or secrets are saved.

Restore puts parents before children, resumes a recorded conversation
by id, and without one uses a "latest in this folder" flag only when
that CLI has one card in the folder (otherwise it starts fresh with a
note on the card; Codex opens its picker). Finished cards come back
stopped with Restart / Continue (Continue resumes the card's own
conversation), and a card whose environment is unavailable is held
stopped with its reason instead of running locally. Cards get an
options menu with "Don't restore this card".

For plugins: the v2 record's two opaque slots are where later extension
points keep per-card state across restarts. A launch contributor's chosen
options are saved in `options[pluginId]` and an environment's ref in
`environment`, both validated and capped at 4 KB, so a restored card can
be prepared or placed again (or held stopped with a reason) without the
core knowing what the values mean.
@howdeploy
howdeploy merged commit 20422d5 into howdeploy:main Sep 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants