* fix(web): force-dismiss indeterminate queued rows on cancel/edit When cancel returns busy for an already-indeterminate QueuedMessagesBar row, keep the optimistic removal instead of re-sticking the row so Edit/X cannot leave the UI dead. Upgrade busy to invoked when queued-state reconcile shows the steer already landed, so Edit toasts instead of prefilling a duplicate. Fixes #1839 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): never prefill Edit when queued cancel returns busy Indeterminate rows can still be a live dispatch. Prefilling on busy risked a duplicate send if the original steer later landed. Keep force-dismiss for Cancel; toast on Edit+busy without restoring the draft. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): keep force-dismissed indeterminate rows for late consume Hide abandoned outcome-unknown rows from the queued bar via queueDismissed so Cancel/Edit still clear the stuck chip, while leaving the message in the window for a later messages-consumed SSE. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): close queueDismissed races on cancel busy and requeue Restore the hidden hold before getQueuedState so concurrent consume SSE can land, skip overwriting it in onSuccess, and clear dismissal when markMessagesRequeued confirms FIFO again. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): undismiss force-held row when cancel busy finds FIFO If getQueuedState reports the localId is queued again after a failed steer, clear queueDismissed so Edit/Cancel return without prefilling. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): preserve queueDismissed across refresh and merges Force-dismiss of indeterminate rows survived Cancel, but latest-page refetch and same-ID merges dropped the client-only flag. Carry it while the authoritative row remains uninvoked and indeterminate. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): clear queueDismissed on reconnect FIFO reconcile Reconnect queued-state reconciliation now markMessagesRequeued for confirmed queuedLocalIds so force-dismissed rows regain Edit/Cancel after a missed messages-requeued SSE. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
HAPI
Run official Claude Code / Codex / Cursor Agent / Grok Build / OpenCode / Kimi / Copilot / Antigravity / Pi / DeepSeek Harness sessions and control them remotely through a Web / PWA / Telegram Mini App.
Why HAPI? HAPI is a local-first alternative to Happy. See Why Not Happy? for the key differences.
Features
- Seamless Handoff - Work locally, switch to remote when needed, switch back anytime. No context loss, no session restart.
- Shared Codex Sessions - Use Codex from your terminal and phone at the same time. Requires Codex 0.154.0+. Usage and limits.
- Native First - HAPI wraps your AI agent instead of replacing it. Same terminal, same experience, same muscle memory.
- AFK Without Stopping - Step away from your desk? Approve AI requests from your phone with one tap.
- Your AI, Your Choice - Claude Code, Codex, Cursor Agent, Grok Build, OpenCode, Kimi, Copilot, Antigravity, Pi, DeepSeek Harness—different agents, one unified workflow.
- Terminal Anywhere - Run commands from your phone or browser, directly connected to the working machine.
- Voice Control - Talk to your AI agent hands-free using the built-in voice assistant.
- Workspace Browser - Opt-in via one or more
hapi runner start --workspace-root <path>flags: browse scoped file trees from the web and start sessions in allowed subdirectories.
Demo
https://github.com/user-attachments/assets/38230353-94c6-4dbe-9c29-b2a2cc457546
Getting Started
npx @twsxtd/hapi hub --relay # start hub with E2E encrypted relay
npx @twsxtd/hapi # choose an agent and start a session
hapi server remains supported as an alias.
Use hapi <agent> [options] to start an agent directly, for example hapi claude
or hapi codex. Scripts must specify the agent explicitly. hapi --help shows
HAPI's commands and supported agents.
The terminal will display a URL and QR code. Scan the QR code with your phone or open the URL to access.
The relay uses WireGuard + TLS for end-to-end encryption. Your data is encrypted from your device to your machine.
For self-hosted options (Cloudflare Tunnel, Tailscale), see Installation
Docs
Native apps (iOS / Android)
Fully native SwiftUI and Kotlin Compose clients are in development under ios/ and android/, with chat, approvals, session creation, files, dictation, and push notifications. Pair using the companion QR code printed by the hub or shown in web Settings, or enter the hub URL and token manually. See the client contract docs.
Build from source
Requires Bun 1.4.0.
bun install
bun run build:single-exe
Credits
HAPI means "哈皮" a Chinese transliteration of Happy. Great credit to the original project.