Skip to content

feat(ui): unify Chat + Session into one Chat tab with a session-mode toggle (#1112)#1340

Merged
vybe merged 1 commit into
devfrom
feature/1112-unify-chat-session-tab
Jun 24, 2026
Merged

feat(ui): unify Chat + Session into one Chat tab with a session-mode toggle (#1112)#1340
vybe merged 1 commit into
devfrom
feature/1112-unify-chat-session-tab

Conversation

@dolho

@dolho dolho commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Collapses the redundant Chat + Session tabs on Agent Detail into a single "Chat" tab carrying a "Session mode" toggle (default ON). The legacy stateless surface stays a first-class, user-selectable mode rather than dead code. theme-ui-ux. No backend change.

Behavior

  • Toggle ONSessionPanel.vue (--resume continuity: memory, tool-result + reasoning state across turns). OFF → legacy stateless ChatPanel.vue. Swaps in-place (v-if on effectiveChatMode), no page nav.
  • Persistence: per-user via localStorage['trinity.chatMode'], default session.
  • Feature-flag / runtime fallback: when session_tab_enabled is off or the runtime lacks --resume (Codex, Codex harness MVP — pluggable agentic execution engine alongside Claude Code #1187), the toggle is hidden and the tab renders legacy ChatPanel only — never zero chat surfaces.
  • Fullscreen: isFullscreenTab now keys on the single 'chat' id (both modes).

Routing

  • ?tab=session aliases to the chat tab (TAB_ALIASES) and hints session mode; ?tab=chat resolves as before. Both land on the unified tab.
  • ExecutionDetail "continue as chat" (?tab=chat&resumeSessionId=…) forces legacy ChatPanel (which owns resumeSessionId) for that landing via a transient, non-persisted routeForcedMode — so an execution-resume doesn't rewrite the user's saved preference. The toggle reflects the forced mode and clicking it clears the override.

Acceptance criteria

  • Exactly one tab labeled "Chat" (never two, never zero)
  • "Session mode" toggle, default ON, with explanatory tooltip
  • ON → SessionPanel, OFF → ChatPanel, in-place swap
  • Choice persists (per-user localStorage, documented)
  • Feature-flag fallback → legacy only, toggle hidden
  • ExecutionDetail deep-link lands on the unified tab; execution-resume forces legacy (defined, no dead tab)
  • ?tab=chat and ?tab=session both resolve to the unified Chat tab
  • No regression to other tabs or isFullscreenTab (single id, both modes)

Notes

  • MobileAdmin needed no change: its openChat is a self-contained mobile chat overlay (API-driven, /chat/sessions + /task), not an AgentDetail tab deep-link — so the tab unification can't land it on a dead surface. (The issue listed it as a deep-link; the code shows otherwise.)
  • Scope decision: mode preference is per-user (one localStorage key) rather than per-agent — simpler, and the toggle reads as a workflow preference.

Validation

SFC compiles (vue/compiler-sfc parse + compileScript); design-token check passes. Full vite build is blocked locally only by an unrelated missing mermaid dep in AgentWorkspace.vue (declared in package.json, absent from this checkout's node_modules) — CI builds with full deps. UI e2e (ui label) exercises the live surface.

Related to #1112

🤖 Generated with Claude Code

…toggle (#1112)

Collapse the redundant Chat + Session tabs on Agent Detail into a single "Chat"
tab carrying a "Session mode" toggle (default ON), keeping the legacy stateless
surface as a first-class user-selectable mode rather than dead code.

- AgentDetail.vue: single `{ id: 'chat' }` tab (drop the separate Session entry).
  New `chatMode` ref ('session'|'legacy', default 'session') persisted per-user in
  localStorage['trinity.chatMode']. `sessionAvailable` = feature flag on AND
  runtime has --resume (not Codex); `effectiveChatMode` forces legacy when the
  Session surface is unavailable and hides the toggle. The toggle swaps
  SessionPanel ↔ ChatPanel in-place (v-if). isFullscreenTab keys on the single
  'chat' id; `?tab=session` aliases to 'chat' (hinting session mode).
- Execution-resume: ExecutionDetail "continue as chat" (?tab=chat&resumeSessionId)
  forces legacy ChatPanel (which owns resume) via a transient, non-persisted
  routeForcedMode — without rewriting the user's saved preference.
- No backend change (session_tab_enabled already exists). MobileAdmin unaffected:
  its openChat is a self-contained mobile chat overlay, not an AgentDetail
  deep-link, so there is nothing to repoint.
- docs: architecture Session Tab block + requirements §5.8 note.

Related to #1112

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validated via /validate-pr: base=dev, conventional commit, focused UI feature (AgentDetail.vue) with architecture.md + requirements.md both updated. Security-clean, no enterprise-doc leak in arch hunk, build green. Note: e2e not triggered (no ui label) — relying on build + thorough acceptance criteria. Approving.

@vybe vybe merged commit b98d0be into dev Jun 24, 2026
18 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.

2 participants