- Introduced TodoListMiddleware to the middleware stack for better task management.
- Updated HITL interrupt configuration to include 'delete' operations requiring approval.
- Implemented error handling for delete operations in read-only and memory backends.
- Enhanced approval prompt formatting to display file paths for delete actions.
- Added tests to ensure delete operations are correctly blocked or prompted for approval.
- Updated dependencies to use deepagents 0.7.0 and langchain 1.5.3 for improved functionality.
* feat(tui): implement completion popup rendering and windowing logic
* feat(tui): enhance completion popup with dynamic row budgeting and CSS adjustments
* Refactor picker widgets to use shared base class for improved code reuse
- Introduced `picker_base.py` to encapsulate common functionality for picker widgets.
- Updated `ModelPickerWidget`, `SkillBrowserWidget`, and `ThreadPickerWidget` to inherit from `PickerWidgetBase`.
- Implemented selection helpers (`first_selectable_index`, `move_selection`) in `picker_base.py` for consistent item navigation.
- Refactored rendering and selection logic in each widget to utilize the new base class methods.
- Added tests for picker functionality to ensure behavior remains consistent post-refactor.
* chore: add pytest-asyncio in auto mode
* test: migrate channel and stream tests to native async
Convert run_async() wrapper tests to plain 'async def test_*' under
pytest-asyncio auto mode. collect_events() in stream_v3_fakes becomes a
coroutine awaited at every call site.
* test: migrate command and model/middleware tests to native async
Convert run_async() wrappers (import, alias, and fixture forms) to plain
'async def test_*'. Multi-call tests merge onto one loop as sequential
awaits; none asserted on loop identity.
* test: migrate TUI, notifier, gateway, and session tests to native async
TUI/notifier/gateway files convert run_async wrappers to plain async
tests. test_sessions.py's unittest.TestCase classes move to
unittest.IsolatedAsyncioTestCase (pytest-asyncio does not await async
methods on plain TestCase; converting blindly would have made ~70 tests
silently vacuous). Its setUpClass keeps a one-shot asyncio.run() since
IsolatedAsyncioTestCase has no async class-level hook. TestLoadingWidget
in test_tui_widgets.py drops its TestCase base for the same reason.
* test: replace direct asyncio.run() calls with native async tests
Convert tests that called asyncio.run() (directly or via a local _run
helper) to plain 'async def test_*'; delete the local helpers.
* test: drop undeclared anyio markers and delete run_async helper
The @pytest.mark.anyio tests relied on anyio being a transitive dep of
httpx; auto-mode pytest-asyncio collects them natively. run_async() and
its fixture are unreferenced after the migration, so remove them —
pytest-asyncio's per-test loop teardown covers the pending-task
cancellation the helper existed for (verified: full suite runs with no
'Event loop is closed' errors or destroyed-task warnings).
* feat(cli): multi-stage slash command completions with subcommand awareness
Phase 1 of #82 — subcommand and argument awareness in completions.
- commands/base.py: add SubCommand dataclass and subcommands/category
ClassVars to the Command ABC. Each SubCommand has name, description,
and optional arguments.
- commands/manager.py: add get_subcommands() and list_subcommands()
methods to expose subcommand metadata for completion rendering.
- commands/implementation/mcp.py: declare 6 subcommands (list, config,
add, edit, remove, install).
- commands/implementation/model_fallback.py: declare 6 subcommands
(list, add, remove, clear, save, help).
- commands/implementation/channel.py: declare 2 subcommands
(status, stop).
- cli/tui_interactive.py: rewrite on_text_area_changed slash-completion
branch. When the user types a command name + trailing space and the
command has subcommands, show subcommand completions instead of hiding
the popup. Filter subcommands by typed prefix in multi-token input.
- commands/implementation/general.py: /help now lists subcommands
below each command that declares them.
Tests: 8 new tests covering SubCommand creation, CommandManager
subcommand lookup, and cross-command verification.
2293 passed baseline, no regressions.
* fix: subcommand completion preserves prefix + prompt_toolkit + tests
- _apply_selected_completion: preserve '/mcp ' prefix when completing
subcommands via _comp_is_subcommand flag
- SlashCommandCompleter (Rich CLI): add subcommand completion support
- Fix trailing-space bug: rstrip prefix before top-level matching
- test_tui_widgets.py: update stub on_input_changed to match multi-stage
logic; add 5 new subcommand tests
- test_command_manager.py: 8 tests for SubCommand + CommandManager
28 passed, 0 failed.
* style: ruff format tui_interactive.py + test_tui_widgets.py
* style: fix RUF012 ClassVar annotation on subcommands lists
* fix: sync test stub, add len>=3 guard, remove exact-match hide
- Sync test stub on_input_changed with real TUI code (remove exact-match
hide for subcommands, add len(parts)>=3 guard)
- Update test_input_changed_exact_subcommand_hides -> shows_confirmation
- Add test_input_changed_three_parts_hides
- Remove unused category ClassVar (din0s: what is this for)
* refactor(commands): extract shared completion engine
Per din0s feedback: one shared completion engine (commands/_completion_engine.py)
that parses text + cursor once, returns structured CompletionCandidate objects
with replace_start/replace_end ranges.
- SlashCommandCompleter (Rich CLI): thin adapter, delegates to engine
- on_text_area_changed (TUI): thin adapter, delegates to engine
- _apply_selected_completion: uses candidate.replace_start/replace_end instead
of _comp_is_subcommand flag
- Tests: engine tested directly (10 new tests), stub methods updated
29 passed, 0 failed.
* style: ruff format
* fix: preserve text after cursor when applying completion
CodeRabbit: replace_start only cuts from start to cursor,
dropping any suffix after the cursor. Use replace_start + replace_end
to correctly splice the replacement while preserving trailing text.
* fix(cli): repair slash-command completion (TUI crash, subcommand bugs, sort)
Apology + context: the previous push shipped a TUI-breaking change
(the new shared engine assumed ``event.text_area.cursor_position``
existed, but ``ChatTextArea`` / ``Changed`` don't expose it). User
caught the crash on ``/``; fixing that surfaced two more bugs in
the engine that din0s had already flagged. This commit addresses
all of them and drops a piece of dead stub code.
## Bug fixes
1. **TUI crash on ``/``** (``tui_interactive.py:2335``)
``event.cursor_position`` doesn't exist on the ``Changed`` event,
and ``ChatTextArea`` (Textual ``TextArea`` subclass) doesn't expose
``cursor_position`` either. Pass ``len(event.text_area.text)``
instead — the user types at the end of the input in practice.
2. **Subcommand trailing-space duplication** (``_completion_engine.py``)
Typing ``/mcp a `` + Tab produced ``/mcp aadd``. The engine
included the trailing space in ``replace_end``; the TUI apply
unconditionally appended ``" "``, producing double-space output.
Fix: ``replace_end`` excludes the trailing space; the TUI apply
checks ``current[replace_end:].startswith(" ")`` and skips the
separator when the suffix already has one.
3. **Subcommand exact-match confirmation noise** (``_completion_engine.py``)
Typing ``/mcp list`` + Tab re-inserted ``list`` and the popup
kept showing the same subcommand. Add a guard mirroring the
top-level exact-match rule: when the only subcommand match is
the prefix itself (no trailing space), return ``empty``.
4. **Alphabetical sort dropped in CLI** (``cli/interactive.py``)
The new completer iterated ``result.candidates`` in registration
order. Re-add ``sorted(result.candidates, key=lambda c: c.text)``.
Same sort added to the TUI for consistency.
## Cleanup
- Drop the dead ``on_input_changed`` method from the ``_StubApp``
test stub (0 call sites) plus the unused ``_slash_commands`` /
``_subcommands`` locals that fed it. This addresses din0s's
comment about the stub duplicating real TUI logic — the inlined
copy is no longer needed since the real completer now routes
through the shared engine.
## Tests
- ``test_engine_exact_subcommand_shows_confirmation`` → renamed to
``test_engine_exact_subcommand_hides`` to match new behavior.
- New: ``test_engine_subcommand_trailing_space_excludes_space_from_range``
and ``test_engine_subcommand_trailing_space_apply_does_not_double_space``.
- All 97 tests in ``test_tui_widgets.py`` pass.
- ``ruff check`` / ``ruff format`` clean.
- Local TUI smoke: ``/`` (no crash, top-level popup), ``/mcp ``
(subcommand popup), ``/mcp a `` + Tab → ``/mcp add ``.
Refs the din0s review comments on PR #273. CLI path tests and the
``category`` ClassVar follow-up are deferred to a separate PR (the
former is a test-suite addition; the latter is already absent from
``base.py`` on the current branch).
* fix: address remaining review items (help duplication, CLI tests, stub sync, docstrings)
- mcp.py: auto-generate help text from subcommands ClassVar (#1)
- tests/test_cli_completion.py: add 9 CLI completer tests (#2c)
- test_tui_widgets.py: sync _apply_selected_completion stub with real code (#4)
- mcp.py + interactive.py: add docstrings to key functions (#8)
* fix: hide completions on exact subcommand match regardless of trailing space
Remove the
ot has_trailing_space guard from the exact-subcommand
check. Previously /mcp list (with trailing space) would still
return candidates, causing Tab to oscillate between adding and removing
the trailing whitespace. Now the engine hides whenever the subcommand
is an exact match, same as the top-level rule.
Added test_engine_exact_subcommand_with_trailing_space_hides to cover
the scenario din0s flagged.
* refactor: use StrEnum for CompletionResult.kind
Replace plain str with CompletionKind(StrEnum) for type safety.
Backward-compatible with existing string comparisons.
* fix: normalize @file completion tuples to CompletionCandidate
complete_file_mention() returns list[tuple[str, str]] but the TUI
rendering/apply code expects objects with .text/.description.
Wrap tuples in CompletionCandidate to prevent AttributeError crash.
---------
Co-authored-by: Xi Zhang <106144707+X-iZhang@users.noreply.github.com>
* feat(memory): migrate to profile memory files
* chore(stream): read profile headings from templates
* fix(display): keep assistant responses if response_text has started
* fix(memory): do not treat failed bootstraps as profile creation
* chore(memory): unlink blank legacy memory
* fix(memory): resolve project_id once
* fix(memory): preserve unreadable profile files
* chore(tui): render streamed narration inline with tool timeline
Update the TUI streaming timeline so assistant text emitted before or
between tool calls is rendered inline where it occurs, rather than being
kept as a single answer bubble above or below the tools.
If the model begins an assistant response and then emits another tool
call, the provisional response is converted into inline narration before
that tool. The final assistant message then renders only the remaining
response suffix, avoiding duplicate text in the completed transcript.
Stop/cancel handling now preserves any active inline narration, appends
the visible stopped marker only to the remaining displayed segment, and
still returns the full normalized stopped response for channel callers.
Completed tools continue to collapse while long runs are active, but
expand again when the turn reaches a final state so the completed
transcript shows the full tool timeline.
* fix(stream): preserve narration around tool timelines
Keep assistant narration attached to the tool call that follows it
instead of folding all streamed text into the final answer block.
Track narrated response segments in stream state, render them before
their corresponding regular or task tool entries, and keep final answers
limited to the response suffix that has not already been shown inline.
Preserve narration across normal completion, stop/error final frames,
sub-agent task calls, and collapsed live tool summaries.
Add regression coverage for pending tools, completed tools, sub-agent
task delegations, collapsed completed/running tool summaries, and final
stop frames.
* fix(tui): finalize inline narration transitions
* test(memory): use canonical project id helper
* feat: Enhance ModelPickerWidget for Ollama integration
- Implemented a sentinel row for "Custom Ollama model..." in ModelPickerWidget, allowing users to input arbitrary model names.
- Updated action handling in ModelPickerWidget to manage transitions between list and input modes.
- Added async model discovery for Ollama models, integrating with the /model command to fetch locally installed models.
- Created tests for Ollama model discovery and ModelPickerWidget behavior, ensuring proper functionality and user experience.
- Refactored validate_ollama_connection and discover_ollama_models for improved error handling and response management.
* fix: Simplify code by removing unnecessary line breaks in ModelPickerWidget and test cases
* fix: Restore globals on set_chat_model failure to prevent half-switched session
* fix: Improve error handling in ModelCommand by restoring globals on failure
* feat(prompt): enhance user interaction with multiple-choice and free-text questions
* refactor(paths): rename MEMORY_DIR to MEMORIES_DIR for consistency
* style(tests): format code for better readability in test cases
* feat(prompt): add validation for 'other' option in user prompt
* feat(prompt): refactor validation logic for user prompts and add skip option
* feat(style): refactor to use shared _PICKER_STYLE from interactive module
* Add status bar and compact summary widgets with context window resolution
- Implemented a shared status bar for CLI and TUI frontends, including helpers for managing session metrics and context windows.
- Created a `CompactSummaryWidget` for displaying manual summaries in a collapsible format.
- Introduced a `CompactingWidget` to indicate ongoing compacting processes.
- Added a base class `TimedStatusWidget` for widgets that require a timer.
- Developed context window resolution helpers to retrieve context window sizes from various model attributes.
- Enhanced tests for context window resolution and status bar functionalities, ensuring accurate behavior across different scenarios.
- Updated existing tests to cover new features and maintain code quality.
* refactor(Channel): simplify lambda function in _send_with_retry method
* feat: enhance context editing logic and improve error handling in StreamState
* refactor(Channel): streamline lambda function in _send_with_retry method
* feat: rename auto-approve option to auto-mode for unattended execution; update checkpoint queries to filter by agent name; improve compatibility validation logic
* feat: rename auto-approve option to auto-mode; update related logic and tests for improved unattended execution
* fix: correct formatting of console message for MCP server configuration status
* feat: add check for None summary_message in _apply_summarization_event to prevent errors
* feat: enhance _load_checkpoint_messages to validate message format and apply summarization event
- Add priority binding for TAB to intercept before Textual's focus_next
- Remove duplicate up/down handling in on_key (now handled by priority
bindings from PR #76)
- Update tests to use cmd_manager.list_commands() instead of removed
_TUI_SLASH_COMMANDS
Closes#57
Co-authored-by: X-iZhang <zacharyzhang2022@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added blank lines for better separation of test cases in multiple test files.
- Reformatted event handling in tests for clarity and consistency.
- Ensured consistent use of multi-line formatting for dictionary arguments in event handling.
- Improved assertions and test descriptions for better understanding.
- Updated test cases across various modules including test_stream_state, test_stream_utils, test_summarization, test_thread_selector, test_tool_error_handler, test_tui_widgets, test_ui_runtime, and test_wechat_channel.
- Introduced UserMessage widget for displaying user input with a styled prompt.
- Updated onboarding steps to include UI backend selection (Rich CLI or Textual TUI).
- Modified EvoScientistConfig to store selected UI backend.
- Enhanced configuration handling to support UI backend environment variable.
- Updated README with new UI backend options and commands.
- Added tests for new UI backend functionality and UserMessage widget.
- Removed obsolete test files and ensured existing tests are updated accordingly.