edc3acc3e2 fix(cursor): requeue user message on transient agent exit (auth, rate limit) (#823)
* fix(cursor): requeue user message on transient agent exit (auth, rate limit)

cursorLegacyRemoteLauncher.runMainLoop popped a user message off the queue
before spawning `agent` and silently discarded it whenever `agent` exited
non-zero (auth expiry, rate limit, transient network). The wrapper logged
the failure at debug level only, never surfaced it to the web UI, and
emitted `ready` as if a normal turn had ended.

Capture stderr from the spawned process; classify exit-1 with a transient
signature (Authentication required, rate limit, ETIMEDOUT, ECONNRESET,
EAI_AGAIN) as recoverable; re-head the message via `queue.unshift`, surface
a friendly banner via `sendSessionEvent({type:'message',...})`, and backoff
~2s before the loop picks it up again. Cap at 5 consecutive transient
failures, after which the message is dropped with a clear "resolve and
resend" event so we never spin forever on a genuinely broken auth.

Non-transient non-zero exits also surface the stderr to the UI now (instead
of only the local ring buffer), so a real crash is visible to the operator.

Backoff is overridable via CURSOR_LEGACY_TRANSIENT_BACKOFF_MS for tests.

Tests cover: success path, transient auth requeue + banner, rate-limit
banner, non-transient crash surfaced without requeue, and the 5-failure
drop cap.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): preserve slash-command isolation on requeue; wait for stderr flush

Two findings from the cold-review bot on the requeue path:

1. `enqueueCursorUserMessage` uses `pushIsolated` for pass-through slash
   commands (e.g. `/compress`) so they never batch with sibling prompts.
   The transient-requeue path used plain `unshift`, which dropped the
   isolate bit and allowed the next collected batch to merge the slash
   command with a sibling - changing command semantics. Add
   `MessageQueue2.unshiftIsolated` and use it when the popped batch was
   isolated or when `parseCursorSpecialCommand` recognises the message.

2. `runAgentProcess` resolved on `child.on('exit', ...)`. Node may emit
   `exit` while the stderr pipe is still draining, so a fast "auth
   required" error printed-and-exited could be classified as
   non-transient with empty stderr and silently drop the user message -
   the exact bug this PR was supposed to fix. Resolve on `close` instead,
   which waits for stdio streams to flush.

Adds a unit test that requeues `/compress` after a transient auth failure
and asserts the second spawn still receives the slash command alone (not
batched with a sibling).

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): restrict transient retry to exit code 1 only

Upstream codex review #823 (Minor): the helper treated any non-zero exit
with matching stderr as transient, which could requeue a signal-killed
(SIGTERM 143, SIGKILL 137) or crashed (SIGABRT 134) process whose stderr
happens to contain a keyword like "rate limit". Documented contract is
exit-1-for-transient; tighten the classifier accordingly.

Adds regression test covering exit 143 + rate-limit stderr → no retry.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): clean up transientBackoff abort listener on timer completion

Upstream codex review #823 (Minor): transientBackoff added an abort
listener with { once: true } but only removed it when the abort fired.
Because the launcher reuses one AbortController, repeated transient
retries accumulated stale listeners until the next abort.

Switch to a single completion path that clears the timer AND removes the
abort listener whichever side wins.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): cap in-memory stderr capture at 8 KB

Upstream codex review #823 (Minor): runAgentProcess accumulated every
stderr chunk for the full child lifetime. A noisy `agent` failure could
grow CLI process memory without bound even though only the first 400
chars are ever displayed. Cap the retained copy at 8 KB; debug log of the
full stream is unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 13:27:35 +08:00
2026-01-04 20:45:15 +08:00
2026-03-29 09:47:17 +08:00
2025-12-16 15:03:50 +08:00

HAPI

Run official Claude Code / Codex / Gemini / OpenCode sessions locally 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.
  • 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, Gemini, OpenCode—different models, 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                 # run claude code

hapi server remains supported as an alias.

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

Build from source

bun install
bun run build:single-exe

Credits

HAPI means "哈皮" a Chinese transliteration of Happy. Great credit to the original project.

S
Description
GitHub 镜像: wu736139669/hapi(每日自动同步)
Readme AGPL-3.0
103 MiB
Languages
TypeScript 99.5%
CSS 0.2%
JavaScript 0.2%
HTML 0.1%