mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
6d2d0d47078b3ce0322d264fe63257f2f23c3d70
* fix(cursor): drop timing heuristic from #784 intercept; scan raw payload (#801 follow-up) PR #801 shipped a two-strategy intercept for the synthetic AskQuestion skip response in legacy stream-json mode. Real-traffic data from a post-merge run shows the marker-match strategy never fires (the converter's `extractToolResult` discards the marker for tool shapes it does not recognize, returning `{}`) and the timing-signature defense-in-depth strategy fires only on false positives - notably the Anthropic Vertex Claude tool calls cursor-agent surfaces in legacy sessions, which all land as `name=unknown` with the `{}` extracted result and frequently complete under the 500 ms threshold. Measured on a single legacy-resumed session (`7b769423`): 1,136 `name=unknown` tool calls, 16 rewritten as `no_input_surface`, zero actual marker strings stored anywhere in the session. The 16 rewrites were legitimate fast tool calls (Anthropic Vertex `toolu_vrtx_*` IDs) mischaracterized as fabricated skip responses. Changes: - Remove the timing-signature heuristic and its supporting state (started-at map, elapsed-ms calculation, latency threshold, test-only state reset). - Move the marker scan from the post-`extractToolResult` output to the raw `tool_call` payload, so it can see the marker on stream-json shapes the converter does not specifically recognize. Function-shaped tools exclude `function.arguments` from the scan to avoid matching agent-controlled input. Other shapes scan the full payload (no agent-input field exists at the top level). - Refresh tests: drop timing-based positive cases, add a marker-in-raw- payload positive case for `name=unknown` shapes, and add a regression that legitimate fast `name=unknown` tool calls without the marker pass through with `status: completed`. - Document scope: this intercept now lives only on the legacy stream- json path, which only resumed pre-ACP sessions hit. New cursor remote sessions go through `cursorAcpBackend` and the `cursor/ask_question` ACP extension method (#799) - immune to this bug. The intercept drains with the legacy session population. Tracking: #784. Builds on #801, complements #799. * fix(cursor): exclude agent input from marker scan; surface top-level Anthropic tool names (Codex P2) Codex flagged a false-positive case on the fork-stage review of this branch (heavygee/hapi#35, P2): an Anthropic tool_use shape with a top-level `name` (e.g. `{id, name: 'TodoWrite', input: { ... }}`) gets labelled `name=unknown` by the converter and passes the AskQuestion gate. If the agent's `input` quotes the synthetic-skip marker - which happens whenever an agent edits or documents this very bug - the intercept would rewrite a perfectly fine TodoWrite as a fabricated skip. Two-part fix: 1. `extractToolName` now reads the top-level `name` field as a final fallback. A real `TodoWrite` / `Bash` / `str_replace_based_edit_tool` surfaces with its actual name and is rejected by the AskQuestion gate before the marker scan runs. The original AskQuestion fabrication case still surfaces as `unknown` (per #784 issue body the name is stripped in the fabricated payload) and remains detectable. 2. Defense in depth: introduce `AGENT_INPUT_KEYS = {input, args, arguments}` and exclude these from the non-function shape's marker scan. Even if a tool reaches this code path with `name=unknown` and the marker buried in its `input`, the intercept won't fire on agent- controlled text. Two new regression tests: - Anthropic tool_use shape `{id, name: 'TodoWrite', input: {todos: [ marker]}}` → passes through with `status: 'completed'`. - `name=unknown` shape with marker only inside `input` → passes through with `status: 'completed'`. All 20/20 tests pass; typecheck clean (cli + web + hub).
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.
Languages
TypeScript
99.5%
CSS
0.2%
JavaScript
0.2%
HTML
0.1%