After extensive debugging and refactoring, the terminal integration issues have been successfully resolved. This documents the critical fixes that made Amp TUI work beautifully.
The terminal integration failures were caused by five critical issues working in combination:
Problem: TerminalProvider was being remounted repeatedly due to:
- Provider placed too deep in component tree (in MainPanel vs App level)
- Parent component re-renders causing provider identity changes
- Cleanup effects running on every unmount triggering cascading re-mounts
Solution:
- Moved TerminalProvider to App.tsx level for stable identity
- Replaced cleanup-on-unmount with beforeunload handler
- Fixed session ID consistency between frontend hooks and backend
Problem: TerminalViewNew.tsx lacked critical rendererReadyRef patterns from the working AmpPtyTerminal.tsx:
- No dimension validation before accessing
this._renderer.value.dimensions - Missing element size checks before mounting
- No proper initialization sequence (open → addon → renderer ready → operations)
Solution:
- Switched from TerminalViewNew.tsx to AmpPtyTerminal.tsx in TuiIntegration
- AmpPtyTerminal contains proven patterns:
rendererReadyRefchecks before dimension access- Element dimension validation (
rect.width > 0 && rect.height > 0) - Proper renderer initialization sequence
- Memory management with buffer caps
Problem: AmpPtyTerminal was calling non-existent Tauri commands:
- Frontend:
terminal_open→ Backend:cmd_start_tui - Frontend:
terminal_write→ Backend:cmd_write_stdin - Frontend:
terminal_resize→ Backend:cmd_resize
Solution: Updated all invoke calls to match actual backend commands with correct parameters
Problem: Session ID format inconsistency prevented event reception:
- Frontend expected:
amp-prod-session - Backend generated:
prod_default - Result:
listen('terminal://data')events never matched
Solution: Changed frontend to use backend format: ${profile}_default
Problem: Terminal elements had no styling, collapsed to 0x0 pixels
Solution: Added import 'xterm/css/xterm.css' to TerminalViewNew.tsx
The AmpPtyTerminal.tsx component contains the essential patterns that must be preserved:
const rendererReadyRef = useRef<boolean>(false)
// Validate renderer dimensions before any operations
const el2 = termRef.current
const rows = el2?.querySelector('.xterm-rows') as HTMLElement | null
const h = rows?.offsetHeight || 0
const w = rows?.offsetWidth || 0
rendererReadyRef.current = h > 0 && w > 0
// Guard all operations with renderer readiness
if (!rendererReadyRef.current) return1. term.open(el) // First: Open terminal
2. term.loadAddon(fit) // Then: Load addons
3. Wait for renderer dimensions // Critical: Validate renderer ready
4. Start PTY only after ready // Finally: Begin data operationsconst xtermOpenedRef = useRef<boolean>(false)
const isCleanedUpRef = useRef<boolean>(false)
// Prevent re-mounting
if (xtermOpenedRef.current) return
if (isCleanedUpRef.current) return// Cap buffer growth to prevent memory leaks
if (writeBufRef.current.length > 1_000_000) {
writeBufRef.current = writeBufRef.current.slice(-500_000)
}// Frontend and backend must use matching format
const sessionId = `${profile}_default` // NOT amp-${profile}-sessionCurrent Working Stack:
App.tsx→TuiIntegration.tsx→AmpPtyTerminal.tsx- TerminalProvider at App level (stable)
- Session ID format:
${profile}_default - Backend commands:
cmd_start_tui,cmd_write_stdin,cmd_resize - Event listening:
terminal://datawith matching session IDs
- Always use AmpPtyTerminal.tsx patterns - they are battle-tested
- Place providers at App level for stable identity
- Validate session ID formats between frontend/backend
- Check renderer readiness before dimension access
- Use beforeunload cleanup instead of unmount cleanup for terminals
- Import xterm CSS - terminals will be invisible without it
- Match command names exactly between frontend invoke calls and backend tauri::command
- Never access renderer dimensions without
rendererReadyRefvalidation - Don't place providers in components that remount frequently
- Don't assume session IDs match - always verify formats
- Don't skip xterm CSS imports - terminals will be invisible
- Don't ignore command parameter mismatches - verify backend signatures
- Don't use different session ID formats in frontend vs backend
Console Debug Output:
[prod] Terminal active, writing data (1024 bytes, renderer ready: true)
[prod] Terminal active, writing data (967 bytes, renderer ready: true)
PTY session opened successfully, result: "prod_default"
Visual Confirmation:
- Amp TUI displays beautifully with full ASCII art
- Terminal shows "Welcome to Amp" with proper styling
- User can interact with terminal (input field responsive)
- No more infinite mounting logs
- No more renderer dimension errors
Date: September 2025 Issue: After restoring Chat/Terminal tabs, the dual-session pattern (Production/Development switching) had critical bugs:
- Development terminal missing textbox
- Both terminals sharing the same session ID
- System breakdown after multiple tab switches
Problem: Both Production and Development terminals used identical session IDs:
// Console logs showed both terminals sharing same ID:
[TerminalView] DUAL SESSION - sessionId: "xzh0enVmZGUNPBPIKl2fG"
// Both prod and dev terminals used same sessionId, causing conflictsRoot Cause: TerminalView.tsx used same currentSession?.id for both terminal instances:
const stableKey = `session-${profile}`; // Same for both prod/devSolution: Create unique, stable keys per terminal profile:
const baseSessionId = currentSession?.id || 'fallback-session';
const stableKey = `${baseSessionId}-${profile}`;
// Results in: "xzh0enVmZGUNPBPIKl2fG-prod" and "xzh0enVmZGUNPBPIKl2fG-dev"Implementation: Each terminal now gets:
- Unique React key preventing remounting conflicts
- Separate PTY sessions via AmpPtyTerminal's internal
sessionIdRef - Independent state preservation when switching tabs
.amp-tui-hiddenCSS class for visibility toggling (not unmounting)
Result:
- ✅ Production terminal state persists across switches
- ✅ Development terminal has working textbox
- ✅ No breakdown after multiple switches
- ✅ True dual-session experience with instant tab switching
Remaining Enhancements (Non-blocking):
- UI sizing optimization (terminal should take full screen height)
- Session management improvements (new/previous sessions)
- Chat session management refinements
The dual-session terminal implementation is STABLE and WORKING BEAUTIFULLY. Both Production and Development terminals maintain independent state and switch instantly without losing context. The solution preserves all critical patterns while enabling true persistent dual-session experience.
Date: September 2025
Status: FULLY INTEGRATED WITH SESSION MANAGEMENT
After integrating the proven TUI patterns with the Phase A session management architecture, we discovered additional critical fixes required for full compatibility:
Problem: Session ID format fixes were required in multiple layers of the architecture:
- AmpPtyTerminal.tsx: Had correct fallback format
${profile}_default - TerminalView.tsx: Was still using legacy UUID format
${profile}_${baseSessionId}_persistent - Backend: Expected
${profile}_defaultor${profile}_{variant}format
Root Cause: The session management integration introduced a new layer (TerminalView) that was overriding the correct session ID format from AmpPtyTerminal.
Solution: Enforced consistent session ID format across all layers:
// TerminalView.tsx - Fixed to use backend-expected format
const sessionId = `${profile}_default`;
// AmpPtyTerminal.tsx - Maintained correct fallback
const sessionIdRef = useRef(sessionId || `${profile}_default`)
// Backend - Already expected this format
let session_id = match variant_id {
Some(variant) => format!("{}_{}", profile, variant),
None => format!("{}_default", profile),
};Result: Eliminated "File exists (os error 17)" errors and enabled proper PTY session creation.
Problem: After integrating with session management, terminal tab switching had two separate issues:
- Production terminal lost input box after switching back (focus issue)
- Development terminal had ASCII art bleeding into input area (sizing issue)
Root Cause: The session management integration required more comprehensive state restoration than the original dual-session implementation.
Solution: Enhanced the useEffect([active, profile]) hook with complete restoration sequence:
useEffect(() => {
if (active && xtermRef.current && !isCleanedUpRef.current) {
// 1. Flush buffered content (existing)
if (writeBufRef.current) {
const data = writeBufRef.current
writeBufRef.current = ''
try { xtermRef.current.write(data) } catch {}
}
// 2. Refresh display (existing)
try { xtermRef.current.refresh(0, xtermRef.current.rows - 1) } catch {}
// 3. Focus terminal (NEW - fixes input box loss)
try { xtermRef.current.focus() } catch {}
// 4. Fit to container (NEW - fixes ASCII bleed)
if (fitRef.current) {
try { (fitRef.current as any)?.fit?.() } catch {}
}
}
}, [active, profile])Result: Perfect terminal switching with maintained input focus and clean layout.
Problem: Phase A TerminalLifecycleManager integration initially caused provider remounting issues when placed incorrectly in the component tree.
Root Cause: The postmortem guidance required TerminalProvider at App level for stable identity, but the new TerminalLifecycleManager was nested incorrectly.
Solution: Corrected provider hierarchy following postmortem patterns:
// App.tsx - Correct hierarchy
<ThemeProvider>
<SessionManagerProvider>
<AmpModeProvider defaultMode="production">
<TerminalProvider> // Stable App-level position
<TerminalLifecycleManager> // Phase A addition
<TerminalManagerProvider> // Nested properlyResult: No provider remounting, stable terminal instances, preserved postmortem patterns.
Complete Integration Evidence:
- ✅ Amp TUI displays beautifully with full ASCII art (preserved)
- ✅ Terminal shows "Welcome to Amp" with proper styling (preserved)
- ✅ Production and Development terminals maintain separate states (preserved)
- ✅ NEW: Session ID format compatible with backend expectations
- ✅ NEW: Perfect tab switching with input focus restoration
- ✅ NEW: Clean layout without ASCII bleed across all switches
- ✅ NEW: Compatible with Phase A session management architecture
- ✅ NEW: Provider hierarchy respects postmortem guidance
Current Working Integration:
SessionManagerProvider (Phase A)
└── TerminalProvider (postmortem requirement - App level)
└── TerminalLifecycleManager (Phase A addition)
└── TerminalManagerProvider (Phase A)
└── TerminalView.tsx (session management layer)
└── AmpPtyTerminal.tsx (proven postmortem component)
Key Integration Principles Validated:
- Preserve all postmortem patterns - AmpPtyTerminal.tsx unchanged in core functionality
- Layer session management cleanly - TerminalView.tsx provides session integration without breaking proven patterns
- Maintain provider stability - TerminalProvider at App level per postmortem guidance
- Enhance state restoration - Additional focus() and fit() calls for session switching
- Enforce format consistency - Session ID format standardized across all layers
Integration Confirmed: The proven TUI patterns from this postmortem are 100% compatible with the Phase A session management architecture when proper layering and format consistency are maintained.
Future Reference: When integrating TUI components with session management:
- Enforce session ID format at ALL layers, not just backend
- Include focus() AND fit() calls in tab switching, not just refresh()
- Maintain TerminalProvider at App level regardless of additional session management layers
- Layer session management ABOVE proven terminal components, don't modify the proven components themselves