Close API clients when the runtime shuts down to release HTTP connection pool resources, and surface the active session ID in /session output.
Cherry-picked from PR #273. Excluded the embedded-wrapper session resolver and memory parser API changes because they need a narrower public API design.
Inject the active permission mode into the runtime system prompt so plan/default/full-auto state is visible to the model.
Refresh the engine model, effort, permission checker, and system prompt when runtime-affecting slash commands update settings.
Based-on: #268
Preserve subscription-auth failures instead of rewriting them as missing API keys, and point Claude/Codex subscription users at the correct login and provider commands.
Fixes#254
Add /model list/add/remove/clear so a single provider profile can expose multiple switchable models in the TUI selector. Also add regression coverage for invalid grep regexes in the Python fallback.\n\nFixes #222\nRefs #218
Issue #195: in coordinator mode, the React TUI and Textual app never
injected `<task-notification>` envelopes after a worker finished.
`run_print_mode` had `_drain_coordinator_async_agents` wired in, but
`ReactBackendHost._process_line` and `TextualApp._process_line` only
called `handle_line` and returned, leaving the contract documented in
the coordinator system prompt (results arrive between turns) unmet.
Extract the drain helpers into `openharness.ui.coordinator_drain` and
invoke `drain_coordinator_async_agents` from both interactive hosts
after `handle_line` when coordinator mode is active. The shared module
also avoids a circular import between `ui.app` and `ui.backend_host`.
* fix(tui): write newline on exit so shell prompt starts on fresh line
Ink hides the cursor during rendering and restores it on exit, but does
not emit a trailing newline. When the TUI process ends, the terminal
cursor sits at the end of the last rendered line, causing the shell
prompt to appear directly concatenated with the TUI output.
Rename restoreCursor → restoreTerminal and append '\n' alongside the
cursor-show escape sequence so the parent shell always receives the
prompt on a clean new line.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(tui): add regression guard for exit newline; update changelog
- Add tests/test_ui/test_tui_exit_sequence.py with two tests:
* test_tui_exit_handler_writes_newline: asserts the cleanup write
includes the trailing \n alongside \x1B[?25h
* test_tui_exit_handler_registered_for_all_signals: asserts cleanup
is registered for 'exit', SIGINT, and SIGTERM
- Add entry to CHANGELOG.md [Unreleased] Fixed section
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(tests): update ohmo cli test inputs for allow_remote_admin_commands prompt
Commit dd1d235 added a new 'Allow explicitly listed administrative slash
commands from remote channels?' confirmation step to the gateway config
wizard, but the four interactive test cases in test_ohmo/test_cli.py were
not updated to provide an answer for it. This caused stdin to be exhausted
and click to emit Abort(), making all four tests fail with exit_code=1.
Add 'n' as the answer for allow_remote_admin_commands in each affected
test, and move the existing 'restart gateway' answer after it in
test_ohmo_config_interactive_can_restart_gateway.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the LLM returns multiple tool calls in one response, query.py
executes them concurrently via asyncio.gather. If more than one tool
requires user confirmation, each concurrent _ask_permission call emits
its own modal_request event. The React frontend overwrites its modal
state on every modal_request (setModal), so only the last dialog is
ever shown to the user. The earlier futures never receive a response
and time out after 300 s, causing silent "Permission denied" errors.
This edge case was not addressed by the recent deadlock fix (69c85e4).
Fix: add _permission_lock (asyncio.Lock) to ReactBackendHost and wrap
_ask_permission with 'async with self._permission_lock'. Only one
permission dialog is live at a time; the next concurrent caller waits
until the current modal is resolved or timed out.
Add test_concurrent_ask_permission_are_serialised to verify that two
concurrent _ask_permission calls are serialised and both resolve correctly.
Co-authored-by: Copilot
Adapted to main's _resolve_api_client_from_settings() architecture.
Added _safe_resolve_auth() wrapper that catches ValueError and prints
a clear message before SystemExit(1).
Co-authored-by: José Maia <glitch-ux@users.noreply.github.com>
A lightweight open-source Python implementation of the Agent Harness architecture.
44x lighter than Claude Code (11K vs 512K lines), 98% core tool coverage.
- 43 tools with Pydantic validation and parallel execution
- Skills system compatible with anthropics/skills (17+ tested)
- Plugin system compatible with claude-code/plugins (12+ tested)
- API retry with exponential backoff
- Multi-level permissions with path rules
- React/Ink TUI with "Oh my Harness!" branding
- 114 unit tests + 6 E2E test suites
- MIT License