Add viewport-driven paging with layout acknowledgements, bounded retries, cancellation gates, and epoch-safe history retention.
Preserve transcript anchors and expansion state, fix tool-group identity collisions, and serialize Android history coordination on Main.
Reduce per-scroll composition and layout work; add native regression tests, CI coverage, and profiling guidance.
#1667 pinned @v1.12 and moved the flaky-relay stream retry tuning from
codex-args into a seeded codex-home config.toml, but the action appends
its own [model_providers.codex-action-responses-proxy] table instead of
merging, so every Codex workflow fails with a TOML duplicate key error.
The overrides cannot be expressed on v1.12 at all.
Pin all three workflows to v1.11, the last release that accepts these
deliberate codex-args overrides under drop-sudo, restoring the original
behavior.
The floating @v1 tag picked up v1.12, whose protected-run validation
rejects model_providers.* overrides passed through codex-args under
drop-sudo, breaking all three Codex workflows. Pin the action to v1.12
and seed a trusted codex-home config.toml with the same settings in an
earlier step instead - the action preserves existing config content.
* fix(web): restore Ctrl+A select-all on the chat page
Chromium's SelectAll collapses to an empty caret when the page contains
a contenteditable (the rich composer) but focus is outside it, so
Ctrl+A + Ctrl+C on the Happy page copied nothing. Take over Ctrl/Cmd+A
at window scope when focus is outside the composer/inputs and select
the message thread manually.
Also removes a duplicate property in markdown-a.test.tsx that broke
`bun typecheck` on upstream/main.
* fix(web): restrict select-all takeover to unshifted Ctrl/Cmd+A; wire e2e spec into CI
Address review findings:
- leave Ctrl+Shift+A to the browser (matches native Chromium, where
the shift variant is unbound)
- run the composer-copy Chromium regression spec in CI alongside
terminal-wrap-fidelity.spec.ts
- move the spec to the root e2e/ dir so the root playwright config
picks it up
* fix(test): stop runner integration suite from leaking detached process trees (#1515)
The default CLI test run included runner.integration.test.ts, which spawns
real detached runner/session process trees. A failing, timed-out, or
interrupted test (or a plain runner stop) left those trees alive under
PID 1 — on the Mac this accumulated ~600 Node/Bun/agent processes and
several GiB of RSS over repeated runs.
Test harness changes only; production runner session-preservation
semantics are untouched:
- Exclude runner.integration.test.ts from the default parallel unit-test
suite; move it into a dedicated serial integration project
(vitest.integration.config.ts, 'bun run test:integration'). The
20-session stress test is opt-in via HAPI_RUN_STRESS_TESTS=true.
- Add a test-owned process/session registry (processRegistry.ts): every
runner, runner-spawned session, and terminal-style child is registered
immediately after spawn; afterEach/afterAll run two-stage cleanup
(logical stopRunnerSession first, then bounded process-tree kill),
followed by a marker sweep for agent grandchildren reparented to PID 1.
- Add a per-run HAPI_TEST_MARKER env stamp + identity/secret env
neutralization for test children (integrationEnv.ts) so outer HAPI/pi
session variables never leak into test processes and the final audit
can recognize test-owned processes by env alone.
- Final suite audit in globalSetup teardown: reap anything still
carrying the run marker and fail with PID/command diagnostics if
anything cannot be reaped, before removing the temp home.
- Regression coverage: a deliberately failing test registers a detached
child and the follow-up audit must find zero test-owned processes.
- CI: replace the dead .env.integration-test step with a dedicated
integration job running the serial project.
* refactor(test): drop unused killByChildProcess import and child field from registry
* chore(test): raise integration hookTimeout to 60s for slow teardown hosts
* fix(test): fail loudly when the process-table audit cannot scan; assert regression child death
Bot review #1521 findings:
- A failed `ps` scan (unsupported flags, buffer exhaustion, permissions)
previously returned [] and silently disabled both teardown audit layers.
It now throws; globalSetup teardown catches the scan error into the
audit error (temp home is still removed) so the run fails visibly.
- The regression audit test cleaned the leak with the reaper before
asserting, and force-killed the fresh marked runner. The failing
test's direct child PID is now asserted dead in afterEach right after
registry cleanup (before the marker sweep), and the audit test stops
its own runner gracefully before reaping.
* fix(test): bound the logical cleanup phase so a hung runner cannot stall the hook
Bot review #1521: stopRunnerSession carries the worker's 60s HTTP timeout
(setup.ts raises HAPI_RUNNER_HTTP_TIMEOUT for the stress test), and the
integration hook timeout is also 60s — N sequential stops could exhaust
the hook budget before the process-tree fallback and marker sweep ran,
recreating the very leak this change prevents.
Logical shutdown is now parallel (Promise.allSettled over all tracked
sessions) and the whole phase (stops + PID resolution) races against a
15s budget, so stage-2 tree-kill and the marker sweep always get their
share of the hook window.
* fix(test): bound graceful runner stop in hooks; keep credentials out of audit diagnostics
Bot review #1521 (follow-up):
- stopRunner()'s HTTP stop can burn the worker-wide 60s timeout on a
hung-but-live runner, starving the marker sweep within the hook budget.
afterEach/afterAll now race the graceful stop against a 10s bound; a
runner that does not stop in time is force-reaped by the sweep (it
carries the run marker) and the next beforeEach's alive-PID guard
ignores any stale state file.
- The env-bearing ps scan (ps eww) was also used for diagnostics, so the
first 500 chars of a short-command process could print inherited
credentials (CLI_API_TOKEN etc.) into teardown error logs. The scan now
only identifies marked PIDs; command lines are fetched separately
without 'e', falling back to '(command unavailable)' instead of the
env dump.
* fix(test): reap runner model-probe orphans before the zero-survivor inspection
Bot review #1521 (Minor): inspect-before-reap. Applying it exposed a real
race: each test's runner legitimately spawns marker-carrying children at
startup (agent acp + agent --list-models model-catalog probes). Stopping
the runner orphans them (ppid 1) with the run marker, so the audit test's
OWN runner polluted the pure inspection with fresh probes spawned after
the failing test's sweep window.
- reapTestOwnedProcesses now re-kills every re-scan iteration instead of
killing once and only re-scanning, so a process that survived its first
SIGKILL (mid-exec) or spawned mid-kill is not given a free pass.
- The regression audit test stops its runner, reaps (clearing its own
legitimate orphan probes), then inspects: anything still marked is a
genuine survivor the bounded reaper could not remove and fails the
suite. Killable leaks from the failing test are already asserted dead
in afterEach before the sweep runs.
* fix(test): strictly bound the marker reaper; make per-test sweep unconditional and verified
Bot review #1521 (follow-up):
- The 10s reap deadline did not bound the awaited per-tree kills: each
killProcessTreeByPid can wait up to 2s per PID, so several stuck
processes could still exceed the 60s hook budget. Every process in a
test-owned tree carries the marker (env is inherited), so tree-walking
is unnecessary: the reaper now SIGKILLs every marked PID found by each
scan, fire-and-forget, and re-scans every 250ms — the deadline strictly
bounds the function.
- The per-test sweep was skipped when the direct-child assertion failed
first, and its survivors were ignored. afterEach now snapshots the
regression-child state BEFORE the unconditional sweep, then verifies
both the registry result and the sweep leftovers.
* fix(test): replace it.fails regression with a direct assertion test
Bot review #1521 (Minor): Vitest applies the it.fails expected-failure
inversion after afterEach, so a broken registry assertion inside the hook
would be masked as an expected failure, and the marker sweep would erase
the evidence before the follow-up audit ran.
The regression is now a normal test that registers a detached child at
spawn time, deliberately performs NO per-test teardown, runs only the
spawn-time registered cleanup, and asserts the child PID is dead. The
afterEach no longer carries the registry-leak assertion (moved into the
test body where it cannot be inverted); the per-test sweep assertion and
the final audit test are unchanged.
* fix(test): bound registry stage-2 tree-kills; require live regression fixture
Bot review #1521 (follow-up):
- Stage-2 killProcessTreeByPid awaits per descendant serially and can
consume the whole 60s hook for a large/stuck tree. Signals are all
delivered synchronously (children first) before any waiting, so racing
the awaits against a 5s budget bounds the phase without skipping any
kill; waitForAllDead still verifies the outcome.
- The regression test could pass vacuously if its fixture exited during
the startup delay (the registry exit listener would remove it before
cleanup). It now asserts the child is alive before running cleanup.
* fix(test): kill registered roots with bare synchronous SIGKILL, no pgrep walk
Bot review #1521 (follow-up): racing the mapped killProcessTreeByPid
calls against a timer does not bound the phase — evaluating the map
invokes each call immediately, and each runs the recursive synchronous
pgrep walk before its first await, which can consume the hook before the
timer, runner stop, or marker sweep run.
Stage-2 now SIGKILLs registered roots directly (fire-and-forget, no
tree walk, no per-PID waits) and waits a bounded 5s for death.
Descendants are reaped by the unconditional marker sweep immediately
afterward — every descendant inherits the run marker, so tree-walking is
unnecessary.
* fix(test): drop duplicate process-death wait in registry cleanup
Bot review #1521 (Minor): the duplicated waitForAllDead delayed the
authoritative marker sweep by another 5s under the exact stuck-process
condition the harness must handle. Keep the single bounded wait; the
afterEach marker sweep remains the guarantee.
Adds GitHub Actions workflow to auto-respond to new issues using OpenAI's Codex action.
Features:
- Triggers on newly opened issues
- Skips issues with duplicate/spam/bot-skip labels or existing bot responses
- Includes comprehensive prompt for context-aware responses
- Enhanced security constraints and response format guidelines
- Checks for existing HAPI Bot responses before processing
Files:
- .github/workflows/issue-auto-response.yml - GitHub Actions workflow
- .github/prompts/issue-auto-response.md - Codex prompt with project context and guidelines
Adds automated deployment to GitHub Pages with custom domain app.hapi.run, and updates vite.config.ts to support dynamic base URL via VITE_BASE_URL environment variable
Consolidate version bumping, building, npm publishing, and git operations into a single release script that handles platform packages first. This solves the issue where optionalDependencies needed platform packages published before bun install could generate complete lockfile hashes.
Changes:
- Created cli/scripts/release-all.ts with support for --dry-run, --publish-npm, and --skip-build flags
- Removed release-it dependency and old release/publish-npm scripts
- Simplified GitHub Actions release workflow to always use --generate-notes
- Deleted obsolete release configuration files (.release-it.json, .release-it.notes.js, publish-npm.ts)
- Update CLI version to 0.1.0
- Change bin script extension from .js to .cjs for ES module compatibility
- Add release-it as dev dependency for version management
- Update all platform-specific binary package versions to 0.1.0
- Enhance release workflow to support custom RELEASE_NOTES.md
- Update CI workflow to package release artifacts with correct naming (hapi-* prefix)
- Generate SHA256 checksums for all artifacts
- Auto-update homebrew-tap repository on release
- Add Installation section to README with Homebrew, npm, and binary options
- Simplify .release-it.json config as CI now handles release process
- Add update-homebrew-formula script to update formula in homebrew-tap
- Add release-artifacts directory to .gitignore
Users can now install via: brew install tiann/tap/hapi