Add ability to resume inactive sessions and automatically continue with new
session ID. Includes message merging for preserving conversation history,
UI improvements to allow sending during inactive state, and database schema
migration. close#87
Move duplicate isObject, asString, asNumber, and safeStringify functions from multiple modules into a centralized shared/src/utils.ts module and update imports across cli, server, and web packages. This eliminates code duplication and improves maintainability.
Add a new ModelSelector component that allows users to choose specific model variants for different AI agents. This includes:
- ModelSelector UI component with agent-specific model options
- Model parameter propagation through API, RPC, and spawning layers
- Support for auto model selection (default behavior)
- Localization strings for English and Chinese
- Type definitions for model options per agent
Previously, useTerminalSocket used a relative path `/terminal` for Socket.IO,
which would connect to the frontend host instead of the actual HAPI server.
Now it uses the baseUrl from context to connect to the correct server, enabling
scenarios like hosting the frontend on GitHub Pages while the server runs elsewhere.
Integrates tunwg (WireGuard tunnel) to enable optional public access
to the hapi server. Tunnel is disabled by default and enabled via
--relay flag or HAPI_TUNNEL=true environment variable.
Users can now run 'hapi server --relay' and get a direct link like:
https://app.hapi.run/?server=https://xxx.relay.hapi.run&token=xxx
- Updated useLongPress.ts to pass click coordinates to onLongPress callback
- Modified SessionList.tsx to use menuAnchorPoint state instead of menuAnchorRef
- Updated SessionActionMenu.tsx to position menu based on anchorPoint coordinates
- Enhanced SessionHeader.tsx to calculate anchorPoint from button position and added stopPropagation to fix toggle issue
The menu now appears at the exact touch/click position instead of being centered or aligned to the button edge.
Implement a visibility-aware notification system that delivers toast messages via SSE to visible browser tabs instead of sending push notifications, reducing unnecessary push requests. Includes VisibilityTracker for monitoring connection visibility, toast UI components, and enhanced SSEManager with toast delivery capability.
Introduces a new message-window-store module to manage message state with
automatic windowing of visible and pending messages. This replaces manual
React Query cache operations with a centralized, observable state system.
close#39
Key changes:
- New MessageWindowState tracks visible/pending messages with size limits
- Automatic trimming of message windows (400 visible, 200 pending messages)
- Pending message buffering when user scrolls away from bottom
- Centralized status updates for optimistic messages
- Thread component simplified with forwarded scroll and pending callbacks
- Removes message count tracking from components
This improves performance for chats with many messages and provides a
cleaner separation of concerns between UI and state management.
Extract permission mode display logic into shared utilities for better reusability and maintainability. Add PermissionModeTone type and related helpers to centralize mode-based styling rules across components. Update components to use new PermissionModeOption type for consistent permission mode presentation.
Add close button to Chrome/Edge install prompt and respect dismissed state
when determining if prompt can be shown. iOS version already had dismiss
capability, so this brings feature parity across all supported browsers.
Implement push notification system with VAPID keys, client service worker integration, and push subscription management. Includes server-side PushService for sending notifications and PushNotifier for reactive event handling, plus client-side usePushNotifications hook and service worker support.
Implements comprehensive session lifecycle management with user-friendly interactions:
Rename: Update session metadata.name via PATCH endpoint with conflict detection
Archive: Abort active sessions via DELETE endpoint with validation
Delete: Permanently remove inactive sessions with cascade cleanup
Backend:
- Store.deleteSession() removes session and cascade-deletes messages
- SyncEngine.renameSession() with concurrency error handling
- SyncEngine.deleteSession() with active session validation
- PATCH /sessions/:id for rename, DELETE /sessions/:id for delete
Frontend Components:
- RenameSessionDialog: Text input with auto-focus and error display
- SessionActionMenu: Modal with rename, archive, delete buttons
- ConfirmDialog: Reusable confirmation with error feedback
- SessionHeader: Menu button (⋮) triggering action menu
- SessionList: Long-press detection triggering item actions
Interactions:
- Long-press on session list items (500ms threshold) opens action menu
- Menu button in session header (non-Telegram environments only)
- Confirmation dialogs with descriptive warnings for destructive actions
- Real-time error display in dialogs on operation failure
- Haptic feedback on long-press via usePlatform hook
Accessibility:
- Keyboard support (Enter/Space) for long-press handler
- Focus management in RenameSessionDialog
- Proper ARIA labels and semantic HTML
Implements bidirectional sync of permission/model modes between CLI sessions and web app. Adds Codex-specific permission modes (read-only, safe-yolo, yolo) alongside Claude's modes. Web can now control CLI session state via RPC set-session-config handler, while CLI broadcasts state changes through keep-alive payloads. UI controls are flavor-aware, showing appropriate modes for Claude vs Codex vs Gemini. Type centralization in api/types eliminates circular dependencies.
Implements full-stack slash command autocomplete with agent-specific built-in commands and user-defined command discovery. Includes React Strict Mode fix for suggestion handling.
Implement comprehensive worktree session support allowing users to spawn sessions in temporary git worktrees. Includes backend worktree management, full-stack integration, and refined UI for session type selection.
Backend:
- Add worktree creation/removal utilities with branch management
- Track worktree metadata (basePath, branch, name, path) in session metadata
- Automatic cleanup of worktrees when sessions fail or exit
- Enhanced error handling with stderr tail logging
UI improvements:
- Redesign session type toggle with improved alignment and spacing
- Move worktree description inline with label for cleaner layout
- Add branch name input field that appears when worktree mode selected
- Auto-focus on worktree input when switching modes
- Reduce gap between radio options from gap-3 to gap-1.5
- Update descriptive text and placeholders for clarity
Integration:
- Thread worktree parameters through API client, RPC handlers, and daemon
- Add worktreeEnv utility to read worktree info from environment
- Update session spawning to support both simple and worktree modes
- Add CLI-side terminal management via Bun.Terminal with TerminalManager
- Implement server-side Socket.IO proxy for terminal I/O between web and CLI
- Create web terminal UI component with xterm.js and support for resize/reconnect
- Add terminal route and navigation button in session chat
- Include comprehensive terminal implementation plan and architecture docs
Adds support for hosting the web UI separately from the hapi server on static hosts (GitHub Pages, Cloudflare Pages). Users can now set a custom server origin via a dialog on the login screen, with the ability to return to same-origin behavior.
Changes include:
- New useServerUrl hook for managing server URL configuration and storage
- Updated API client to support baseUrl parameter for all requests
- Enhanced login UI with server picker dialog (top-right button)
- Auth system now keys tokens per baseUrl to support multiple servers
- SSE connection updated to use configured baseUrl
- Documentation updates for standalone hosting setup
Debounce the sync banner to only show if sync takes longer than 300ms.
This prevents the banner from flashing on quick syncs while still showing
for slower operations.
Changes:
- Added DEBOUNCE_MS constant (300ms)
- Changed doStartSync to delay banner display by 300ms
- Changed endSync to immediately clear all timeouts
- Renamed endSyncTimeoutRef to startDelayTimeoutRef (now for debounce)
Implement two key UX improvements:
1. Syncing banner with visibility tracking:
- New useSyncingState hook manages syncing state with safety timeout
- Banner shows when SSE connects/reconnects
- Auto-hides after 10s to prevent stuck spinner
- Smart visibility detection to suppress banner when returning from background
2. Smart scroll behavior for chat thread:
- Only auto-scrolls when user is near bottom (<120px threshold)
- Shows "X new messages" indicator when user is reading history
- Smooth scroll to bottom with indicator button
- Resets state on session change
Files:
- New: useSyncingState hook for syncing state management
- New: SyncingBanner component for non-blocking indicator
- Modified: App.tsx integrates syncing with SSE handling
- Modified: HappyThread implements smart scroll with dynamic autoScroll
- Modified: index.css adds spinner and bounce-in animations
Adds touch device detection via media query (pointer: coarse) to prevent
triggering virtual keyboard on mobile. Input auto-focus is now conditional
on desktop environments where it improves keyboard-based interactions.
- Add back buttons to MachinesPage and SpawnPage for non-Telegram environments
- Fix navigation after session creation to clear spawn flow from history
- Update useAppGoBack hook to use explicit path navigation for consistent behavior
Implements comprehensive ACP backend enabling integration with ACP-compliant agents like Gemini. Includes stdio transport, message handling, permission flow, and registry for agent management. Adds new 'hapi gemini' command to launch ACP agent sessions.
Replace state-based screen navigation with proper URL-based routing. This includes:
- New router configuration with routes for sessions, machines, and spawn pages
- App context provider to share API and token across the app
- useAppGoBack hook for handling browser and Telegram back navigation
- Refactored App component to render outlet and use router hooks
- Memory history for Telegram app, browser history for web
Replace manual state management with TanStack Query (React Query) for more robust server state handling. This refactoring introduces:
- New hooks for queries: useSessions, useSession, useMessages, useMachines
- New hooks for mutations: useSendMessage, useSessionActions, useSpawnSession
- Centralized query client with optimized configuration (5s staleTime, disabled window focus refetch)
- Query key factory for consistent cache invalidation
- Improved message synchronization via socket events with cache updates
- Optimistic updates for message sending with retry capability
- Simplified App.tsx by removing manual state management logic
- Integrated React Query devtools in development mode
This enables automatic cache management, better error handling, and a foundation for more sophisticated data fetching patterns.
The conditional Telegram SDK loading (commit 4606ba2) caused dark mode to
break in non-Telegram browsers. Added dark mode CSS variable overrides with
proper fallbacks, and restored the module-level applyTheme() call that applies
theme before React renders, preventing a flash of incorrect styling.
- web/src/index.css: Added dark mode overrides for primary colors (--app-bg,
--app-fg, --app-hint, --app-link, --app-button, --app-button-text,
--app-secondary-bg) with system-appropriate dark fallbacks when Telegram
theme variables are unavailable
- web/src/hooks/useTheme.ts: Restored module-level applyTheme(currentScheme)
call to ensure theme is applied before React renders
Add lazy loading of Telegram SDK only when running inside Telegram Mini App
environment instead of unconditionally loading in all browsers. Includes:
- New environment detection functions: isTelegramEnvironment(), isTelegramApp()
- Dynamic SDK loading with 3s timeout via loadTelegramSdk()
- Removed static script tag from index.html
- Made haptic feedback lazy (SDK check on each call)
- Made theme listeners lazy (attached in initializeTheme())
- Updated all components to use unified detection functions
This prevents SDK load time overhead in regular browser environments while
ensuring the app works correctly both in Telegram and browser contexts.