Commit Graph
278 Commits
Author SHA1 Message Date
b9d28f4d30 fix(cli): include ~/.codex/skills in user skill discovery (#482) (#487)
The skill listing only scanned ~/.agents/skills and ~/.claude/skills
for user-level skills, ignoring ~/.codex/skills where Codex users
commonly store their skills. Add ~/.codex/skills to getUserSkillsRoots()
so these skills appear in the web UI $ autocomplete.

Hidden directories (starting with .) inside the skills root are still
skipped (e.g., .system/).

via [HAPI](https://hapi.run)

Co-authored-by: HAPI <noreply@hapi.run>
2026-04-17 13:16:45 +08:00
Junmo KimandGitHub e6eaff83c5 fix(hub,cli): forward permissionMode on session resume (#460)
* feat(hub,cli): forward permissionMode on session resume

When a session is resumed, the cached permissionMode is now forwarded
through the Hub → Runner → CLI pipeline via a new --permission-mode
flag. Previously the mode was lost on resume, resetting to 'default'.

Each CLI flavor validates the flag value against its own allowed
permission modes (e.g. CLAUDE_PERMISSION_MODES) and rejects unknown
values. The existing --yolo flag is preserved as a shorthand.

* refactor(cli): extract buildCliArgs from startRunner

Extract the CLI argument construction logic into a standalone
exported function so it can be unit-tested independently.
No behavior change.

* test(cli): add buildCliArgs unit tests for --permission-mode

Verify that the runner correctly forwards valid permission modes
via --permission-mode, rejects invalid values, and falls back to
--yolo when no permission mode is set.

* fix(cli): let --permission-mode take precedence over --yolo

When both flags are present, --permission-mode was silently
overwritten by a later --yolo. Guard legacy flag branches with
a hasExplicitPermissionMode check so the explicit flag wins.
2026-04-15 11:13:21 +08:00
ShujakuinandGitHub 0bffb03b05 feat(cli): support optional extra headers for hub requests (#445)
* feat(cli): support extra headers for hub requests

* fix(types): normalize missing session fields to null

* refactor(cli): simplify socket extra headers config
2026-04-14 13:54:51 +08:00
Haoqing WangandGitHub 74377fafee fix(cli): prevent CLAUDE_CODE_ENTRYPOINT leak into local spawn (#452)
extractSDKMetadataAsync() calls query() which sets
CLAUDE_CODE_ENTRYPOINT='sdk-ts' on the current process env.
When claudeLocal() later spawns the claude CLI, the child
inherits this env var, causing Claude Code to treat the
session as SDK-launched. This makes the session invisible
to `claude --resume`.

Strip CLAUDE_CODE_ENTRYPOINT from the child env so the
local spawn uses its own default entrypoint.

Closes #450
2026-04-14 13:29:05 +08:00
ShujakuinandGitHub fadc0def8f fix(cli): honor HAPI_HOSTNAME in session metadata (#449) 2026-04-13 20:25:10 +08:00
tuanaiseoandGitHub 03b6a667c0 fix(security): potential command injection on windows due to `she (#439)
The process is spawned with `shell: process.platform === 'win32'` while including dynamic values (e.g., `opts.sessionId`) in `args`. On Windows, shell invocation can introduce command parsing/injection risks if arguments are not strictly validated/escaped.

Affected files: opencodeLocal.ts

Signed-off-by: tuanaiseo <221258316+tuanaiseo@users.noreply.github.com>
2026-04-11 21:37:27 +08:00
Haoqing WangandGitHub c62a1eb151 fix(cli,hub): resolve typecheck errors in codex reasoning effort (#432)
* fix(cli,hub): resolve typecheck errors in codex reasoning effort and notification test

- Cast `getModelReasoningEffort()` return (string | null) to
  `ReasoningEffort | undefined` at three call sites in
  codexLocalLauncher.ts and runCodex.ts where the narrower type is
  expected.
- Add missing `modelReasoningEffort: null` default in
  notificationHub.test.ts to satisfy the Session type contract.

These errors were introduced in 79a13d2 and have been failing CI on
main since 2026-04-10.

* fix(cli): add missing getModelReasoningEffort to test mock session

The test stub in codexLocalLauncher.test.ts was missing the
getModelReasoningEffort method added in 79a13d2, causing runtime
TypeError in CI.
2026-04-11 16:44:06 +08:00
weishu 79a13d26c6 Fix Codex reasoning effort resume and updates 2026-04-10 11:50:03 +08:00
Junmo KimandGitHub 2eae161139 fix: filter rate_limit_event from Claude Remote/Local chat paths (#423) 2026-04-09 20:16:34 +08:00
iRonManandGitHub 56925f8b98 fix(codex): handle remote request_user_input approvals (#382) 2026-04-07 12:04:52 +08:00
weishu 3ef7b8c7cd Release version 0.16.6 2026-04-07 10:44:28 +08:00
Junmo KimandGitHub 2f61852a9e refactor: extract local agent spawn helper and unify process tree cleanup (#410) 2026-04-07 09:50:40 +08:00
9eb0dacf75 fix(cli): filter raw SSE event JSON from leaking into chat messages (#405)
* fix(cli): filter raw SSE event JSON from leaking into chat messages

Two types of internal JSON were appearing as visible text in Telegram
Mini App and web chat:

1. `rate_limit_event` — the rate limit parser returned `null` for
   unknown statuses, causing raw JSON to pass through as assistant text.
   Changed to `{ suppress: true }` so all rate_limit_event variants are
   handled; new statuses that need display can be added explicitly.

2. `{ type: "output", data: { ... } }` — internal session metadata
   envelopes leaked through the ACP text chunk pipeline. Added an
   `isInternalEventJson` filter that catches JSON objects with known
   internal envelope types (output, event, queue-operation) before they
   enter the text buffer.

Closes #386

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): narrow internal event filter to match only leaked metadata shape

Address review feedback: the broad type-based filter could suppress
legitimate assistant JSON with type "event" or "queue-operation".

Narrow the check to only match the specific leaked metadata envelope:
{ type: "output", data: { parentUuid, sessionId, userType } }

Add negative tests confirming other JSON types pass through.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): suppress malformed rate_limit_event without resetsAt

Address review: rate_limit_event payloads missing resetsAt still leaked
as raw JSON because parseRateLimitText returned null before reaching the
unknown-status suppress. Move the allowed check before the resetsAt
guard and suppress malformed payloads instead of passing them through.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): handle parentUuid: null in internal event filter

Root/first-message metadata envelopes have parentUuid: null rather than
a string, so the filter missed them. Accept both string and null.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* test(cli): add e2e regression tests for metadata envelope filtering

Add AcpMessageHandler tests that verify leaked { type: "output", data }
metadata envelopes (both parentUuid string and null) are dropped before
reaching the text buffer.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): clear buffered prefix when cumulative metadata chunk arrives

When a leaked metadata envelope arrives as cumulative streaming chunks
(first an incomplete JSON prefix, then the full blob), the filter
dropped the full chunk but left the prefix in bufferedText. Clear the
buffer when the detected internal JSON starts with the buffered prefix.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): round resetsAt to integer for pipe-delimited format

The web-side regex uses \d+ to parse the timestamp, so a float value
would silently fail to match. Apply Math.round to ensure integer output.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): clear buffered prefix for cumulative rate_limit_event chunks

The prefix-clearing logic only applied to the isInternalEventJson
branch but not to the parseRateLimitText branch, so cumulative
rate_limit_event chunks could leave a raw JSON prefix in the buffer.

Hoist the prefix check before both filters and apply uniformly.
Add regression tests for suppressed and displayable cumulative
rate_limit_event scenarios.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

---------

Co-authored-by: HAPI <noreply@hapi.run>
2026-04-06 20:42:28 +08:00
bca7521823 fix(cli): continue execution after plan mode in YOLO/bypassPermissions (#406)
* fix(cli): continue execution after plan mode in YOLO/bypassPermissions

In YOLO mode (bypassPermissions), exit_plan_mode was auto-approved like
any other tool, skipping the PLAN_FAKE_RESTART injection that tells the
agent to continue. Combined with isAborted() always returning true for
exit_plan_mode, claudeRemote exited the query loop and stalled waiting
for user input.

Fix: in the bypassPermissions branch of handleToolCall, intercept
exit_plan_mode specifically — inject PLAN_FAKE_RESTART into the message
queue and return deny with PLAN_FAKE_REJECT, matching the behavior of
the normal approval flow.

Closes #172

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* test(cli): remove unused isPlanTool helper

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

---------

Co-authored-by: HAPI <noreply@hapi.run>
2026-04-06 20:42:06 +08:00
c02f392d81 fix(cli): correct Homebrew formula URL for Linux x64 baseline build (#408)
The release workflow produces `hapi-linux-x64-baseline.tar.gz` (from
bun-linux-x64-baseline target), but the Homebrew formula generator wrote
the URL as `hapi-linux-x64.tar.gz`, causing `brew install` to fail on
Linux x64 with a 404.

Closes #365

via [HAPI](https://hapi.run)

Co-authored-by: HAPI <noreply@hapi.run>
2026-04-06 20:41:41 +08:00
Junmo KimandGitHub 00ba610ab0 feat: display rate limit warnings instead of raw JSON (#388)
* refactor(web): extract normalizeTimestamp helper in presentation

Extract the shared seconds-vs-milliseconds normalization logic into
a private `normalizeTimestamp()` helper. No behavior change —
`formatUnixTimestamp()` produces identical output.

* refactor(web): return AgentEvent from parseClaudeUsageLimit

Change return type from `number | null` to `AgentEvent | null` so
the caller doesn't need to construct the event object. No behavior
change — the same `limit-reached` event is produced.

* feat(cli): convert rate_limit_event to standardized text format

Parse undocumented Claude `rate_limit_event` JSON in the CLI adapter
layer (AcpMessageHandler) before it reaches the web.

Converted text format (pipe-delimited):
  - "Claude AI usage limit warning|{ts}|{pct}|{rateLimitType}"
  - "Claude AI usage limit reached|{ts}|{rateLimitType}"

Status handling:
  - `allowed_warning` → warning text with utilization and limit type
  - `rejected` → reached text with limit type
  - `allowed` → silently suppressed (noise)
  - unknown statuses → passed through as-is (forward-compatible)

* feat(web): display rate limit warnings with limit type

Parse standardized pipe-delimited text from the CLI adapter into
`limit-warning` and `limit-reached` events, displaying the rate
limit type (5-hour, 7-day) when available.

- `limit-warning`: "⚠️ Usage limit 90% (5-hour) · resets 2:00 PM"
- `limit-reached`: " Usage limit reached (5-hour) until 4/2/2026"
- Backward compatible: `limit-reached` without limitType still works

The `reached` regex uses `(?:\|([^|]*))?$` to optionally match the
limitType field, maintaining compatibility with the existing format.

* refactor(cli): move rate limit parsing out of flushText

Remove rate limit detection from flushText() back to plain buffer
flush. The next commit will re-add parsing at the chunk level
(handleUpdate) where it can intercept before buffer merging.

Includes failing tests that demonstrate the mixed-chunk bug:
when a rate_limit_event chunk arrives in the same turn as normal
text, the JSON leaks into the merged buffer.

* fix(cli): intercept rate_limit_event at chunk level, not flush

Move rate limit detection from flushText() to the agentMessageChunk
handler so it fires before the chunk enters the shared text buffer.

Previously, a rate_limit_event chunk arriving in the same turn as
normal text would merge into bufferedText and leak as raw JSON.
Now the chunk is intercepted individually, the existing buffer is
flushed first (preserving prior text), and the converted message
is emitted separately.

* fix(cli): skip flush when suppressing allowed rate_limit_event

Only flush the text buffer when the parsed event will actually be
displayed. Suppressed events (e.g. status: 'allowed') now return
immediately without flushing, preventing a text → allowed → text
sequence from splitting one answer into two agent-text blocks.

* fix(web): include limitType in limit-reached reconcile key

Without this, reprocessing a message from the old format (no
limitType) to the new typed format reuses the stale block and
the (5-hour)/(7-day) suffix never appears.
2026-04-03 08:25:22 +08:00
Junmo KimandGitHub 4eb88c5d7e feat(gemini): support mid-session model change (#379) 2026-04-01 11:15:11 +08:00
weishu fa0ce1ca94 Release version 0.16.5 2026-03-31 18:53:38 +08:00
weishu 4248540fb4 auto approve in yolo mode for codex 2026-03-31 18:51:34 +08:00
weishu ca7cb9ac90 auto approve title tool for codex 2026-03-31 18:36:31 +08:00
Junmo KimandGitHub 3a073dc4a9 fix(gemini): wire --resume flag through to Gemini backend (#378) 2026-03-31 10:46:21 +08:00
Wong ChihungandGitHub 1f67d36c06 feat(models): support new gemini models and codex gpt-5.4-mini (#376) 2026-03-29 21:33:33 +08:00
xyzhang626andGitHub 63337873c3 fix(claude): handle async background task notifications in remote mode (#354) 2026-03-26 08:07:06 +08:00
Haoqing WangandGitHub 279f75815e fix(cli): prevent system-injected messages from appearing as user role (#361) 2026-03-26 08:06:30 +08:00
Haoqing WangandGitHub 6384697b03 fix(cli): filter isMeta and isCompactSummary messages in local and remote mode (#359) 2026-03-25 05:36:41 +08:00
a200fe9628 feat(claude): add effort setting parity with model across stack (#353)
Co-authored-by: Xiaoyi <xiaoyizhang@microsoft.com>
2026-03-24 21:15:48 +08:00
Haoqing WangandGitHub 30265fdc25 fix(cli): filter invisible system messages in local mode (#351) 2026-03-24 19:21:17 +08:00
weishu 7da6f7c5c5 Release version 0.16.4 2026-03-24 13:04:42 +08:00
weishu 304789620b Fix reason effort setting not working. close #346, close #333 2026-03-24 12:04:52 +08:00
Haoqing WangandGitHub b802092bf7 fix(cli): treat any exit code as expected when abort was requested (#347) 2026-03-24 02:41:13 +08:00
Haoqing WangandGitHub 8eea49f9da fix(cli): fix process exit handling deadlock and error masking in Claude SDK (#343) 2026-03-23 14:56:45 +08:00
lifu963andGitHub 895654ddf6 fix(terminal): prevent infinite reconnect loop on Windows hosts (#336) 2026-03-21 21:45:40 +08:00
pppobearandGitHub ae39fc5e77 Fix Codex default approval policy for remote sessions (#328) 2026-03-20 18:45:18 +08:00
weishu a02f908dc5 fix test 2026-03-20 13:19:10 +08:00
weishu 32f05d99a0 Release version 0.16.3 2026-03-20 12:17:35 +08:00
Junmo KimandGitHub d76b1a6ac0 refactor: introduce model-agnostic agent interfaces (#323) 2026-03-20 08:45:32 +08:00
ROOOOandGitHub 397ef35f5c fix(cli): preserve requested cwd for runner-spawned sessions (#315) 2026-03-19 15:15:41 +08:00
ROOOOandGitHub 987010f447 fix(codex): pass session cwd to app-server (#316) 2026-03-19 15:12:11 +08:00
ROOOOandGitHub f967bd9928 fix(runner): restart when hub identity changes (#303)
* fix(runner): restart when hub identity changes

* fix(runner): fail closed on missing identity
2026-03-18 10:43:36 +08:00
2ecd56dbe8 fix(gemini): notify hub when session is aborted (#307) (#308)
Co-authored-by: Junmo Kim <me@junmo.kim>
2026-03-18 08:12:57 +08:00
ROOOOandGitHub a30bb398e1 fix(codex): keep local session timeout non-fatal (#304) 2026-03-18 07:27:23 +08:00
ROOOOandGitHub f24ea4fea8 fix(codex): improve reused session adoption (#305) 2026-03-18 07:26:58 +08:00
ROOOOandGitHub 9c043b42da fix(cli): load configured API URL in auth status (#306) 2026-03-18 07:26:21 +08:00
ROOOOandGitHub cb09f0b898 feat: add codex reasoning effort option (#297) 2026-03-17 22:55:55 +08:00
ROOOOandGitHub caa76826f4 fix(cli): preserve invoked cwd for local launcher (#299) 2026-03-17 22:55:23 +08:00
weishu 34f931ef59 remove codex mcp backend 2026-03-16 21:50:09 +08:00
weishu 16829b7c78 Add support for codex plan mode 2026-03-16 20:48:39 +08:00
weishu 329d28a93c remove , using instead 2026-03-16 18:29:09 +08:00
weishu 02c8e12e80 unify model selection 2026-03-16 18:29:09 +08:00
weishu 1f02bb7de1 Fix codex approve policy 2026-03-16 13:00:09 +08:00