Skip to content

feat(*): tools reliability fixes, coding profile, and main sync - #1

Merged
TongLi31 merged 18 commits into
TongLi31:raven-xfrom
ethanwwwwwwww:raven-x
Aug 3, 2026
Merged

feat(*): tools reliability fixes, coding profile, and main sync#1
TongLi31 merged 18 commits into
TongLi31:raven-xfrom
ethanwwwwwwww:raven-x

Conversation

@ethanwwwwwwww

Copy link
Copy Markdown
Collaborator

Summary

Merges the TB2.1 tools-improvement work into raven-x and syncs with
upstream main (litellm provider refactor).

  • detached background jobs; background+session now routes to a named
    detached job (4 verifier-time server deaths in tb21 attributed)
  • deny patterns anchored to command position; timeout clamp with
    configurable tools.exec.maxTimeout; actionable tool errors with
    did-you-mean and tool-name repair
  • same-call breaker (all-failed streaks only), history pairing repair,
    mid-turn session checkpoints
  • coding system-prompt profile (opencode-style, raven tool routing);
    the SE-discipline block is hoisted so BOTH profiles render it,
    keeping it active under the eval harness's profile=coding
  • full analysis and re-verification results in
    docs/tools-improvement-tb21.md (pypi-server and
    configure-git-webserver flip green on re-run)

Verification

uv run pytest tests/test_agent_loop_tool_loop_break.py
tests/test_agent_loop_turn_durability.py tests/test_segments.py
tests/test_tool_registry_suggestions.py tests/test_sandbox_unit.py
(111 passed) and config suites (75 passed); ruff check/format clean.
Known pre-existing failures unrelated to this branch: MCP sandbox guard
test, optional channel-dependency collection errors.

cyfyifanchen and others added 18 commits July 26, 2026 09:48
## Summary

- Remove the Star Us and Star History section from the English README.
- Remove the corresponding Star section from the Chinese README.
- Remove both sections from their tables of contents.

The embedded Star History chart still does not render reliably, so the
promotional section is being removed instead of retaining a broken
README surface.

## Type

- [ ] Fix
- [ ] Feature
- [x] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

- `make check-commits` passed.
- `PR_TITLE='docs: remove star history section' make check-pr-title`
passed.
- `make check-large-files` passed.
- Python test target passed: 58 tests.
- The isolated ModelPicker test passed.
- The full TUI run passed 901 tests and failed 1 unrelated ModelPicker
timing assertion. The failure reproduces only in the full suite and is
outside this README-only diff.

- [ ] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [x] User-facing docs or screenshots are updated when needed

## Risk

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

N/A
## Summary

Add first-class MiniMax Token Plan OAuth support for both Global and CN
regions.

This change adds:

- `minimax_global` and `minimax_cn` provider entries with curated
MiniMax-M3 and M2.7 models.
- OAuth 2.0 device authorization with PKCE S256 and strict state
validation.
- The shared MiniMax public client ID `coding-plan-cli` with the `openid
profile coding_plan` scope.
- Region-specific OAuth and Anthropic-compatible API endpoints.
- Proactive access-token refresh with a five-minute buffer,
cross-process locking, transient retry handling, and atomic
refresh-token persistence.
- Separate Global and CN credential files, isolated from existing Codex
OAuth storage.
- Runtime token refresh and MiniMax authentication headers for both
normal and streaming requests.
- CLI login commands, onboarding, provider status/test/reset support,
TUI setup detection, and model-picker entries.
- English and Chinese documentation updates.

Public model references remain under `minimax-global/` and
`minimax-cn/`. The runtime translates them to the Anthropic-compatible
LiteLLM route only when sending a request, so provider selection
continues to use the correct MiniMax OAuth credential.

The implementation accepts MiniMax expiry values expressed as relative
seconds, Unix epoch seconds, or Unix epoch milliseconds. OAuth-returned
verification and resource URLs are restricted to the expected HTTPS
hosts before use.

## Type

- [ ] Fix
- [x] Feature
- [x] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

- `uv run pytest tests/test_minimax_oauth.py
tests/test_cli_provider_commands.py tests/test_provider_catalog.py
tests/test_config_update_providers.py tests/test_tui_rpc_model.py
tests/test_tui_rpc_setup.py tests/test_cli_status_commands.py -q -x` -
143 passed.
- Focused MiniMax onboarding tests - 3 passed.
- `uv run ruff check raven tests` - passed.
- `uv run ruff format --check raven tests` - 761 files already
formatted.
- `make check-large-files` - passed.
- `make check-commits` - passed.
- Two independent read-only reviews completed; the final review found no
blocking issues.
- The full test collection was not run because the local environment
does not include the optional `dingtalk_stream` channel dependency. All
provider, OAuth, onboarding, TUI setup, status, and configuration tests
relevant to this change passed.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [x] User-facing docs or screenshots are updated when needed

## Risk

- OAuth credentials are stored in region-specific files with restrictive
permissions and atomic replacement.
- Refresh operations use a cross-process lock and persist rotated
refresh tokens before exposing them to the runtime.
- OAuth verification and API resource URLs are validated against
region-specific host allowlists.
- Existing `minimax` API-key configuration remains unchanged; the new
OAuth providers use separate slugs and storage.
- Rollback consists of reverting this change. Existing API-key provider
configuration and credentials are not migrated or modified.

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

N/A

- Live OAuth smoke test: MiniMax Global MiniMax-M3 returned "MiniMax
OAuth works" successfully after normalizing the Anthropic-compatible
resource URL.

---------

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
…nd-AI#224)

## Summary

CI ran `make test-python` over 4 hardcoded files, so ~268 of 272
top-level test files never executed on any PR. A bare `uv run pytest`
could not stand in for it either: collection aborted with 7 errors
(channel-adapter tests importing optional SDKs at module level), and
`testpaths` pulled `tests/integration/` into every run.

This makes the whole unit suite the blocking CI gate.

- `Makefile`: `test-python` runs the whole unit tree instead of a
hardcoded file list.
- `ci.yml`: the `python` job splits into `lint-python` and a `unit` job
running the whole suite on ubuntu / py3.12; the matrix block is kept so
more versions are one list entry away.
- `pyproject.toml`: register `integration` / `e2e` markers, enable
`--strict-markers` / `--strict-config`, exclude `tests/integration` via
`norecursedirs`, and default to `-m "not integration and not e2e"` so a
bare `pytest` is safe.
- Guard the 7 channel-adapter tests with `pytest.importorskip`; CI
installs `--all-extras`, so they run there rather than skip.
- Relocate 9 integration / real-resource files out of the flat unit tree
into `tests/integration/`, renaming the token_wise benchmarks to the
`real_llm` kind required by AGENTS.md section 5.2.

Turning the suite on surfaced 44 pre-existing failures, all fixed here:

- 29 from `asyncio.get_event_loop().run_until_complete()` in three
producer test files, which picks up a closed loop once other async tests
have run; now `asyncio.run()`.
- 2 stale `run_turn` stubs missing the `inline_tool_stream` keyword the
production callers pass.
- 12 from cross-test state leaking through
`raven.config.loader._current_config_path`, litellm's global logging
handlers, and an ambient dependency on a populated `~/.raven` in the TUI
init-error tests.
- 1 flaky concurrency test whose timing bound left only 0.05s of margin.

The full run also recovered 38 genuine unit tests under
`tests/tui/autotest` that a directory-level exclusion would have
orphaned; they are selected normally now, with the 24
subprocess-spawning e2e tests deselected by marker.

Deliberately out of scope: an integration / nightly CI tier (real-LLM
runs are hard to make meaningful in CI and cost money -- to be designed
separately), and the naming and assertion debt tracked in EverMind-AI#223.

## Type

- [x] CI / tooling

## Verification

```
uv run --all-extras pytest -q
# 4611 passed, 24 deselected in 193s

bash .claude/scripts/preflight_ci.sh
# ALL PREFLIGHT CHECKS PASSED (commitlint, large files, pre-commit, ruff, tests)
```

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally

## Risk

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

No production code changes -- test files, test configuration, and CI
workflow only.

The repository ruleset currently declares no required status checks, so
renaming the job blocks nothing. If the `unit` job should become a merge
gate, add `unit (py3.12 / ubuntu-latest)` to the ruleset separately.

## Related Issues

Closes EverMind-AI#222

---------

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
…Mind-AI#226)

## Summary

Two packaging defects, plus the CI simplification the second one
unblocks.

**`raven[sandbox]` could not be installed on Windows.** boxlite
publishes wheels for macOS and Linux only -- it is a microVM runtime
built on KVM / Hypervisor.framework, and its PyPI classifiers say so --
but our declaration carried no platform marker, so the install failed
outright:

```
error: Distribution `boxlite==0.9.5` can't be installed because it doesn't have
a source distribution or wheel for the current platform
```

The code already degrades gracefully when boxlite is absent:
`sandbox/interfaces.py` keeps the shared types importable without it,
and both call sites probe with `try: import boxlite / except
ImportError`. So a Windows user should have gotten a Raven that reports
the sandbox as unavailable, not a failed install. `python-socks` and
`matrix-nio` already carry the same `sys_platform != 'win32'` marker;
boxlite now does too.

**The `retrieval` extra had no consumer.** It declared torch and
transformers, which nothing imports -- a whole-tree grep finds them only
in that declaration, two comments, and a doc note, with no dynamic
loading and no doc telling users to install it. It dates back to the
initial commit. Keeping it cost a multi-hundred-megabyte download per
environment and added a native crash surface of the kind `cli/_exit.py`
documents. Removed via `uv remove`; the lockfile drops from 220 to 194
packages.

**CI follows.** With torch out of the extras, `--all-extras` now
resolves to what the hand-maintained `channels + tools + sandbox` list
covered, so the unit job goes back to it and stays correct as extras
change. Every job also gains `timeout-minutes`.

A `windows-latest` cell was added to measure whether the marker fix was
enough to run the suite there. It is not: the install now succeeds, but
the suite hit 121 failures by 28 percent, across sixteen files, from
systemic platform assumptions (assertions hardcoding forward slashes,
fcntl locking, socket and subprocess differences). That is a port, not a
fix, so the cell was dropped. This matches comparable projects:
hermes-agent runs every job on ubuntu and substitutes a blocking static
check for Windows-unsafe primitives, and openclaw's PR-blocking workflow
is ubuntu-only with Windows behind workflow_dispatch. The dedicated
Windows self-upgrade job still covers the install path.

The macos cell is dropped for the same reason, one level milder: it
reported the same failures as ubuntu on every run while ubuntu
additionally caught two Linux-specific problems it could not, and
development happens on macos anyway. The unit job is now a single ubuntu
cell, with the matrix block kept so another entry is one list item away.

## Type

- [x] Bug fix

## Verification

```
uv run --all-extras pytest -q
# 4611 passed, 24 deselected -- same count as before, torch was never used

uv run python -c "import importlib.metadata as m; m.version('torch')"
# PackageNotFoundError, and boxlite still installs on this macOS host

bash .claude/scripts/preflight_ci.sh
# ALL PREFLIGHT CHECKS PASSED
```

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally

## Risk

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

`raven[retrieval]` stops being a valid extra. Nothing in the repo or the
docs referenced it, and installing it only pulled torch into the
environment without any code path using it, so no behaviour changes.

The Windows matrix experiment is described above and left out of the
final state; both dependency fixes stand on their own.

## Related Issues

Closes EverMind-AI#225

---------

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
…rMind-AI#220)

## Summary

The TUI already read `update_behind` / `update_command` out of the
session init
bundle, but nothing on the Python side ever filled those fields, so the
hint
never appeared. This wires up the missing half and moves where it shows.

**The check**

`raven/cli/update_notice.py` compares the running version against a
small
cache in `~/.raven/update_check.json`:

- `raven tui` calls `maybe_refresh_async()` once per launch. It spawns a
daemon
thread only when the cache is missing or older than 24h, so a normal
launch
  touches the network at most once a day and never blocks startup.
- The gateway only *reads* that cache when it builds the session info
bundle
  (`_default_session_info`). Reading is pure and fast; no network on the
session-create path, which is why the check is cached rather than live.
- Every failure path is swallowed: no cache, unparseable version,
corrupt JSON,
  network error, rate limit. An update hint must never break startup.

The tradeoff is staleness by one launch: the first launch after a
release
lands refreshes the cache, and the hint shows on the next launch.

**Where it shows**

The status bar's right slot, in the warn color, replacing the cwd/branch
label
while an update is available. No extra line, no layout shift.

The old banner block in `branding.tsx` is removed. The banner is the
first
thing on screen at startup and an upgrade nudge does not deserve that
weight.
`update_behind` is now a flag rather than a commit count, so the text is
version-agnostic (`Update available - run raven upgrade`) instead of the
old
`N commits behind`.

## Type

- [ ] Fix
- [x] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

```
uv run ruff check raven/ tests/                  # All checks passed
uv run ruff format --check raven/ tests/         # 760 files already formatted
uv run pytest tests/test_cli_update_notice.py \
  tests/test_cli_tui_commands.py \
  tests/test_tui_rpc_session.py -q               # 95 passed
cd ui-tui && npm run type-check                  # clean
cd ui-tui && npm test                            # 904 passed | 3 skipped
cd ui-tui && npm run lint                        # 0 errors
cd ui-tui && npx prettier --check "src/**/*.{ts,tsx}"   # clean
```

New tests cover the cache read/write and refresh throttle
(`tests/test_cli_update_notice.py`), the status-bar slot swap
(`ui-tui/src/__tests__/statusRule.test.tsx`), and the banner no longer
rendering the block (`branding.test.tsx`).

Also exercised the five edge cases by hand: absent cache, newer cached
release, cache equal to current, unparseable version string, and a
corrupt
cache file. Only the second one returns a notice; the rest return
`None`.

The lint warning reported on `appChrome.tsx`
(`react-hooks/exhaustive-deps`,
line 275) is pre-existing on `main` and untouched by this change.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

The GitHub releases fetch reuses
`upgrade_commands._fetch_latest_release`, so
no new network surface or credential handling. The cache file holds a
version
string and a timestamp only.

Wire-compatible: `update_behind` / `update_command` were already
optional
fields in the info bundle, and a client that never receives them behaves
as
before. Rollback is reverting this commit; the feature degrades to its
current
state, which is showing nothing.

To see it locally:

```
printf '{"latest_version":"9.9.9","checked_at":%s}' "$(date +%s)" > ~/.raven/update_check.json
```

## Related Issues

N/A

---------

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
…I#217)

## Summary

Reworks the TUI transcript so a turn reads as a sequence of steps
instead of
interleaved thinking blocks and tool lines. Every model call becomes one
"episode" holding its reasoning, narration and tools.

**Rendering**

- A step with no narration collapses to one summary line ("reasoning for
8s,
  read 2 files"); a narrated or running step shows a reasoning fold, the
  narration as prose, then its tool rows.
- Reasoning expands while it streams and folds once the step produces
anything
  visible (narration, a tool, or the answer text).
- A run of same-name calls renders as a tree: one header plus a child
row per
  call, so a burst of parallel searches no longer fills the screen.
- In-flight tools report elapsed time; finished ones keep their measured
span.
- Rows are bound to the transcript width and clipped with an ellipsis.
Without
the bound, long details overflowed the container and the terminal
soft-wrapped
  them, which looked like stray blank lines and edge-cut text.

**Tool display contract**

Tools now own how they appear. `Tool.display_call` and the new
`ToolResult`
(`model_text` plus optional `display_text`) separate what the model
reads from
what the transcript shows:

- `ask_user` renders its question and the answer instead of a raw
arguments
blob, pairing each question with its answer when several are asked at
once.
- Tools with no explicit verb derive a readable one from their own name,
so a
newly added tool renders sensibly with no changes to the verb table (it
is an
  overrides list, not a required registry).
- The call preview picks the query, question or command out of the
arguments
  instead of the first value, which could be a numeric flag.

**Also**

- Episodes are the default transcript style; `/transcript legacy` stays
as an
  escape hatch and the legacy path is untouched.
- An interrupt commits the collapsed episode view instead of dumping the
raw
  expanded segments.
- The virtual-height estimate accounts for episode rows. Without it the
  transcript reserved too few rows and left stale cells on screen.
- Markdown table columns fit the available width with CJK-aware
clipping.
A cell wider than its column is truncated with an ellipsis and cannot be
expanded in the TUI (it used to soft-wrap, which kept every character
but
looked broken), and inline markup inside a cell is flattened to plain
text
  rather than rendered.
- The deep-research offer states that a regular search is the user's
chosen
path, not a failure, so the model stops narrating it as a broken
fallback.

**Also in this diff, beyond the stated scope**

- The queued-message panel is restyled: the `queued (N)` header is gone
and the
edit hints moved to a trailing line. The count stays derivable from the
window
  plus `...and N more`, and the edited row is still highlighted.
- User messages get their own tier: an accent chevron and bold text, so
a prompt
  reads as a prompt next to the assistant's prose.
- `transcriptGutterWidth` takes the tool glyph, so the user and
assistant gutters
  are derived from one place instead of two hardcoded widths.

## Review follow-ups

Addressed in the second commit, by the reviewer's IDs:

- **R1** an interrupt with no `episode.start` dropped the accrued
segments and
tool trail; it now falls back to the legacy trail exactly like the
completion
  path, with a regression test.
- **R2** the `ToolResult` / `display_call` contract had no Python
coverage. Adds
`tests/test_ask_user_tool.py` and a loop test asserting
`add_tool_result` gets
  `model_text` while `tool.complete` carries `display_text`.
- **R3** the transcript-mode comment was wrong on all three claims.
- **R4** `ToolRegistry.execute` now unwraps at the boundary and returns
`ToolOutput`, a `str` subclass carrying the display string, so the
sentinel
action executor, subagent manager, curator and tracing can no longer
receive a
  dataclass. `isinstance(.*ToolResult)` hits the registry only.
- **R7** drops `episodeLabel`, `turnSummary`, `toolLine` and
`episodeIndex`.
- **R8** the height estimate counts the inter-step margin row.
- **R9** (mid-turn `/transcript` switch) and **R10** (clarify answer
beyond the
200-char preview) are left for a follow-up: both add state or a wire
field
  rather than fixing this diff.

## Type

- [ ] Fix
- [x] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

```
cd ui-tui && npm run type-check          # clean
cd ui-tui && npm test                    # 927 passed | 3 skipped
cd ui-tui && npm run lint                # 0 errors, no warnings in touched files
cd ui-tui && npx prettier --check "src/**/*.{ts,tsx}"   # clean
uv run ruff check raven/ tests/          # All checks passed
uv run ruff format --check raven/ tests/ # 758 files already formatted
make test-python                         # 4621 passed, 2 pre-existing failures
cd ui-tui && npx vitest run              # 925 passed | 3 skipped
```

New tests cover the episode commit path (including interrupt), the tool
summary
and verb fallback, the argument preview, and the rendered layout.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

The wire gains one optional `tool.start` field (`display`); older
payloads
without it fall back to the generic preview. `execute` may now return
either a
string or a `ToolResult`, so existing tools are unaffected. Rollback is
`/transcript legacy` for the view, or reverting this commit.

Known follow-ups, not addressed here: occasional screen garbling seen
when
messages are queued (root cause not yet identified), and `web_search`
being
registered even without an API key.

## Related Issues

N/A

---------

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
## Summary

A read-only audit of all 298 test files found the suite in good shape --
exactly one genuinely fake test -- with the real debt sitting in file
names and a scattering of assertions that passed for the wrong reason.
This clears that, and fixes the bugs found while clearing it.

**Three dead tests dealt with.** Two removed because neither could ever
assert anything:

- `test_chat_e2e.py` was a module-level skip over three
`NotImplementedError` stubs. The wire-up it waited for has shipped
(`tui_commands` instantiates `SubscriptionEmitter`) and the proposal doc
it cited is gone.
- `test_tui_exit_e2e.py` asserted that building the agent loop leaves
lancedb's thread live. It does not: the loop is constructed but never
started, and the memory plugin talks to everos over HTTP, so lancedb is
not even imported. The assertion could not hold in any configuration.

The third, `slashParity.test.ts`, shelled out to python for a
`raven_cli.commands.COMMAND_REGISTRY` that no longer exists, and skipped
all three of its cases when the import failed -- three skips on every
run. The parity it wanted needs no python: `createSlashHandler.ts:45-51`
routes a slash locally when `findSlashCommand` resolves it and otherwise
hands it to `slash.exec`, a CLI subprocess a mutation cannot reach the
live session from. It now asserts that predicate directly.
`NATIVE_MUTATING_COMMANDS` and the three-way `classifyRoute` went with
it: all six names in that set are also in the local registry, so the
native branch could not change any outcome.

**19 files renamed** off phase, ticket and bug codes (AGENTS.md section
5.1), including one reversed CLI name and two integration files moved
onto a legal `<kind>`. Reference updates ride along:
`.pre-commit-config.yaml` names one of these files in a
`detect-private-key` exclude, and three docs pages point at them.

**Assertions that passed for the wrong reason, tightened**: eight
over-broad `pytest.raises(Exception)` narrowed to the type each site
actually raises; an `except` clause that swallowed the `AssertionError`
its own fakes use as a tripwire; a `hasattr` probe replaced by binding a
broker and checking it stuck; and a recall assertion that read as a pass
on an empty result set.

**Dead weight removed**: ten unused locals, three dead code blocks, six
`importorskip` guards on hard dependencies, two hardcoded `/tmp` paths,
and ticket codes left in docstrings after the renames.

**Two coverage gaps the deletions exposed are now closed**:

- `tests/test_cli_exit.py` pins the hard-exit guard, which had no tests
at all.
- The TUI chat e2e gains a multi-turn case. It reads the persisted
session rather than the screen, because a prompt is echoed into the
transcript the moment it is typed -- screen-scraping for a planted word
passes even when history is broken.

**Three timing bugs fixed in the TUI e2e tests.** They keyed off the
banner, which paints about seven seconds before the app accepts input,
and submitted the moment text was typed. Dropped prompts made the
pipeline look dead in one test, and made a negative assertion (`assert
not leak_detected`) pass vacuously in another -- no prompt means no
streaming means no leak.

**Gaps in the two features that landed during review**, small enough to
close here: the `session.py` wiring that fills `update_available` /
`update_command` (removing it left the suite green),
`_upgrade_command_works` (a fixture stubbed it to `True` everywhere), an
`episodeSummary` case with an empty test body, and `clipToWidth` /
`hasMeaningfulReasoning`, which had no coverage at all. The larger items
from that audit are tracked in EverMind-AI#229.

**Upstream review attributions dropped from ui-tui comments.** 36
mentions of another project's review thread (#19835 as "Copilot round-N
review"), plus #18994, #19194 and #14045 -- none resolve to anything
here, and AGENTS.md section 1.1 rules out comments pointing at
information only visible elsewhere. The rationale each carried is kept;
four `describe()` titles lose a trailing `(#18994)`. Three went further
and documented a python counterpart that does not exist: `platform.ts`
credited key-spelling normalization to `raven_cli/voice.py` (no such
module -- `raven/tui_rpc/methods/_stubs.py` answers `voice.toggle` with
"voice not supported in Raven v0.1"), and `useConfigSync.ts` plus its
test credited an `interrupt` default to `raven_cli/config.py` and
`tui_gateway/server.py::_load_busy_input_mode` (`busy_input_mode`
appears nowhere in python; neither module exists).

**One flaky test fixed, caught by this PR's own CI run.**
`test_stdio_no_executor_does_not_raise` set `command="true"` and let
`connect_mcp_servers` really spawn it, so the result depended on how a
live child process raced the MCP handshake -- it failed once on CI in
the full-suite run while passing 30 out of 30 standalone runs locally.
It also asserted only that whatever came back was not a
`SandboxInitError`. The transport now raises instead of running (the
idiom its sibling test already uses) and the test asserts the stdio
branch was reached.

Review follow-ups, folded in: the hard-exit test now skips rather than
asserting no lancedb thread exists process-wide, `tests/conftest.py` no
longer credits the pytest segfault to a lancedb thread the suite never
starts (raven imports lancedb nowhere), and the status-bar patterns the
two TUI e2e tests wait on live in one module instead of a hand-mirrored
copy per file.

Every new or changed assertion was checked by mutating the code under
test and confirming the assertion fails.

## Type

- [x] Others

Test files, test configuration, and comments. The only non-test files
touched are ui-tui comments and four `describe()` titles -- no behaviour
change.

## Verification

```
uv run --all-extras pytest -q
# 4654 passed, 25 deselected

cd ui-tui && npx vitest run
# 936 passed, no skips

uv run --all-extras pytest tests/tui/autotest -m e2e -q
# passes locally with tui-use installed and ui-tui built

bash .claude/scripts/preflight_ci.sh
# ALL PREFLIGHT CHECKS PASSED (including the TUI lane)
```

Mutation checks, each confirming the new assertion fails when the code
under test is broken: the `session.py` nudge wiring removed;
`clipToWidth` replaced with identity; `hasMeaningfulReasoning` forced to
`true`; a never-sent prompt added to the multi-turn session assertion.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally

## Risk

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

No behaviour changes: the non-test diff is comments only. The renames
touch git history for 19 test files; EverMind-AI#208 is the only open PR affected
and has been notified.

Two known items are deliberately left alone: `test_dogfood_whitelist.py`
is flaky when the whole file runs (tracked in EverMind-AI#228, unchanged here), and
the `lancedb_finalization_hazard` gate in `commands.py` never fires
today -- its test docstring says so explicitly, so the coverage does not
read as an argument for keeping it.

## Related Issues

Closes EverMind-AI#223

---------

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
…erMind-AI#236)

## Summary

`test_dogfood_whitelist.py` was reported as flaky: run the file and a
few cases fail, a different few each time. It was not flaky. Three
defects stacked, and each one hid the next.

**Every one of the 14 output assertions was vacuous.** The expected
pattern is matched against the whole screen, which includes the composer
echoing the command just typed. 13 of the 14 were satisfied by that echo
alone -- `skill list` on `skill`, `cron show` on `cron`, `sandbox list`
on `sandbox`. The fourteenth, `status` (`OpenRouter|Model:`), was
satisfied by the welcome frame's own status line. This is a pure string
property, no timing involved.

**Readiness keyed off the banner**, which paints before the app accepts
input. Typing right after it, the composer was observed still showing
its placeholder -- the keystrokes were dropped entirely. Defect 1 is why
that was invisible: a dropped command still satisfied the assertion.

**Ctrl+C is a ladder, not an exit key.** In `useInputHandlers.ts` a busy
UI cancels the turn, a non-empty composer clears the input, and only
then does it quit. The test pressed it once, so any command that left
the UI busy timed out in `expect_exit`. That is the reported flakiness,
and pressing until the process exits takes the file from `3 failed, 4
passed, 4 xfailed, 3 xpassed` to `0 failed`.

The three together also explain the experiment recorded in EverMind-AI#228 as
inexplicable: fixing readiness alone made one case fail
*deterministically*, because the command then really submitted and
really ran a turn, so the UI was busy when the single Ctrl+C arrived.

Ruled out with instrumentation rather than argument: a pytest plugin
logging `tui-use list` at every setup and teardown shows no session ever
leaked between cases, and `tui-use info` retains `Status: exited`
indefinitely, so `expect_exit` cannot lose a code it already had.

## What else the audit turned up

**Three commands in the whitelist do not exist.** `skill refresh` and
`skill stats` (the CLI has only `skill list` / `skill get`), and `cron
show`, whose rename to `cron get` shipped long ago. All three were
answered with "unknown command" and passed anyway.

**The same three defects were in every other file in the tier**, so they
are fixed there too, and the seven `xfail(strict=False)` markers are
gone -- their reason blamed a Raven overlay-exit defect, but with a
correct exit sequence all seven pass.

**The README was the source.** Its reference example keyed readiness off
the banner, asserted `OpenRouter|Model:`, and exited with a single
Ctrl+C. Every file that copied it inherited all three. It now documents
the correct sequence, states plainly what the tier cannot do, and drops
eight references to documents that no longer exist.

**The tier is now scoped to what only it can prove.** Dogfood covers
four output shapes instead of 12 commands: `createSlashHandler.ts`
routes every dispatched slash through the same `slash.exec` call, and
the Python side already has 31 dedicated tests, so per-command
repetition proved nothing the first case did not.
`test_e2e_tui_status_slash.py` is deleted (same spawn, same command,
same assertion as the dogfood `status` case) and so is
`test_e2e_raven_chat_cli.py`, an xfail-strict placeholder spawning a
`raven chat` that is neither implemented nor planned -- its tripwire
moves to `tests/test_cli_smoke.py`, where it costs nothing and actually
runs.

**The Ctrl+C ladder moves into vitest.** `decideCtrlC` is now a pure
function, the shape this file already used for
`applyVoiceRecordResponse`, so the rungs are pinned without a PTY or a
live turn. Hook behaviour is unchanged: `isTurnActive()` is still only
consulted on the busy path.

A static sweep of all 301 test files for assertions their own input can
satisfy found exactly one more, in `test_session_fork_help`, which
asserted `"fork"` against `fork --help` output whose usage line echoes
the subcommand name.

## Type

- [x] Others

Tests, test configuration and comments, plus one behaviour-preserving
extraction in `ui-tui/src/app/useInputHandlers.ts`.

## Verification

```
uv run --all-extras pytest -q
# 4655 passed, 13 deselected

cd ui-tui && npm test
# 943 passed

uv run --all-extras pytest tests/tui/autotest -m e2e
# 13 passed, no xfail, no skips, 2m36s   (was 22 passed / 4 xfailed / 3 xpassed in 4m21s)

bash .claude/scripts/preflight_ci.sh
# ALL PREFLIGHT CHECKS PASSED (including the TUI lane)
```

The dogfood file was run three consecutive times: 12 passed each, no
flakes. Every new assertion was mutation-checked: restoring `skill
refresh` fails on missing output; pointing a pattern at `OpenRouter`
trips the idle-screen guard; spawning `sh -c 'cat > /dev/null'` makes
the cat-echo assertion fail where the old one passed; registering a
`chat` command trips the new tripwire; three separate mutations of the
Ctrl+C ladder each turn vitest red.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally

## Risk

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

The only non-test change is extracting `decideCtrlC` out of the
`useInputHandlers` Ctrl+C branch; the branch order and the guard on
`isTurnActive()` are preserved, and three mutations confirm the new
tests fail when it is not.

Left alone deliberately: no CI job runs this tier, so these runs rest on
local execution -- the repo's existing gap, now stated in the README
rather than implied. The tier also still lives at
`tests/tui/autotest/tests/`, a third location AGENTS.md section 5 does
not sanction; splitting it into `tests/` and `tests/integration/` needs
its own PR, since it is renames plus a fixture move with no bearing on
this fix.

## Related Issues

Closes EverMind-AI#228

---------

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
…ger (EverMind-AI#238)

## Summary

`raven.cli.commands.run` hard-exited past interpreter finalization
whenever lancedb's `LanceDBBackgroundEventLoop` thread was live. That
thread cannot exist in this process: lancedb is a transitive dependency
only, nothing under `raven/` imports it, memory talks to everos over
HTTP, and everos runs out-of-process. `torch`, named alongside it in the
agent-loop comment, is neither declared nor imported either. The probe
returned False in every configuration, so the branch was unreachable.

Removed the probe in `_exit.py`, the `if` it fed in `commands.py`, and
the comment in `agent_commands.py` claiming the exit chokepoint covers
that hazard.

`flush_and_hard_exit` stays: `tests/conftest.py` calls it on CI, where a
fully green suite was observed exiting 139 on Linux. Its module
docstring now describes that caller instead of the lancedb story, and
records why the gate went.

Deleting the whole `except SystemExit` block is behaviour-preserving.
With the probe always False it reduced to `except SystemExit: raise`,
and the exit-code normalization inside it only ever fed the hard-exit
call -- never the re-raised exception.

## Type

- [x] Others

Dead-code removal in the CLI exit path, plus the two tests that
exercised the removed probe.

## Verification

`CliRunner` invokes the Typer app directly and never reaches `run()`, so
the console-script entry point was smoked by hand:

```
raven --version                 -> 0
raven skill get (missing arg)   -> 2
raven nosuchcmd                 -> 2
raven status                    -> 0
unparseable ~/.raven/config.json -> one clean warning line, no traceback
```

```
uv run --all-extras pytest -q
# 4653 passed, 13 deselected   (4655 before: the two probe tests are gone,
#                               the four covering flush_and_hard_exit remain)

bash .claude/scripts/preflight_ci.sh
# ALL PREFLIGHT CHECKS PASSED
```

Also grepped the whole repo, including `benchmarks/` and `scripts/`, for
`lancedb_finalization_hazard` and `LanceDBBackgroundEventLoop`: no
references remain.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally

## Risk

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

The change is a deletion in a path no test host reaches, so the
console-script smoke above is the real verification rather than the
suite. If an embedded local-lancedb mode ever lands, the guard would
need reinstating -- deliberately, and against a probe that can actually
fire.

## Related Issues

Closes EverMind-AI#232

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
…ng it (EverMind-AI#250)

## Summary

AGENTS.md section 3.7 reproduced `.github/pull_request_template.md`
inline, and
the copy had drifted from it. Every heading differed, and the documented
`Type`
list had no `Refactor` or `CI / tooling` -- so a refactor PR following
this file
had to pick `Others`, while the real template offers the right box. The
same
section tells you the checkbox mirrors the commit type, which a list
missing two
commit types cannot do.

Reference the file rather than restating it: a copy is what drifts. The
filling
rules now name the template's actual sections and say what each is for
--
`Verification` wants the commands and their result rather than a claim
that they
ran, `Related Issues` distinguishes `Fixes #NNN` from a bare reference,
and
`Type` is a mirror of the commit type rather than a fresh judgement
call.

Net effect is 37 lines removed and 7 added: the rules that are not in
the
template stay, the duplicate of the template goes.

## Type

- [ ] Fix
- [ ] Feature
- [x] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

```
uv run pre-commit run --files AGENTS.md      # passed
make check-large-files                        # passed
npx commitlint --from origin/main --to HEAD --config commitlint.config.cjs
grep -rn "Change description|Type of change|Checklists" AGENTS.md   # no matches left
```

`CLAUDE.md` is a symlink to `AGENTS.md`, so both read the corrected
text.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [x] User-facing docs or screenshots are updated when needed

## Risk

Documentation only; no code path changes. The advice it replaces was
already
unusable as written, so following the new text cannot produce a worse PR
than
following the old one. Rollback is a revert.

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

Fixes EverMind-AI#241

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
…I#249)

## Summary

Provider management used to be a whitelist: a registry of 21 vendors
that
decided who could be configured at all, plus a second execution path
that
bypassed LiteLLM entirely for knn routing. This makes LiteLLM the single
execution layer and reduces the registry to what LiteLLM cannot tell us.

**knn routing now runs on LiteLLM.** It needed several endpoints alive
in one
process, which `CustomProvider` provided by talking to the endpoints
directly --
at the cost of native streaming, retries and tool-calling.
`PerModelProvider`
now builds one `LiteLLMProvider` per endpoint instead, and
`custom_provider.py`
is deleted. knn is a decision layer again; execution is LiteLLM
everywhere.

**Any vendor LiteLLM supports is now configurable.** A key under the
vendor's
name plus a `<vendor>/<model>` id is enough -- 134 vendors instead of
21, with
no registry entry required. `provider list`, `raven status`, the startup
gate
and the TUI model picker all report them, which they previously could
not: a
working setup read as unconfigured, so Raven kept pushing the user into
a wizard
that then refused to configure that vendor. Names LiteLLM has never
heard of are
still rejected as typos, and a typo no longer makes Raven unstartable.

**A provider now has one name.** It used to have two -- ours and
LiteLLM's --
reconciled by a `litellm_prefix` field read at every site that compared
a name
or a model-id prefix. That field conflated two opposite facts: LiteLLM
spelling
the same vendor differently (`vllm` -> `hosted_vllm`) versus a provider
reached
through another vendor's driver (SiliconFlow over OpenAI's API). "Is
this prefix
mine?" has opposite answers for the two, and deriving the distinction
from
whether the prefix happened to be another entry's name was correct only
by
coincidence. LiteLLM's spelling is now the provider's own name,
`litellm_prefix`
is gone, and only a borrowed driver states `via_driver` -- which is
absent from
`route_names` by construction, so an id asking for `openai/` can no
longer be
answered with SiliconFlow's key. `zhipu` is renamed to `zai` on the same
basis.

Four facts were each implemented at several call sites and had already
drifted
apart -- how a model id splits into a prefix, how a name is spelled for
comparison, which names refer to a provider, and how a provider's config
section is found. Each now has exactly one implementation, in
`registry.py` or
`ProvidersConfig.get`.

### Credential routing

Several paths could hand one vendor's key to another. All are fixed
here:

- a gateway's model prefix was read from the raw field rather than the
derived
one, so a gateway whose name is already LiteLLM's declared nothing and
the
prefix was dropped -- sending the gateway's key to the vendor named in
the
  model id;
- an explicit prefix could be overridden by a keyword match, so
`deepinfra/deepseek-ai/DeepSeek-V3` put DeepInfra's key in
`DEEPSEEK_API_KEY`
  and rewrote the model id;
- a bare id naming an unconfigured vendor fell back to any credentialed
direct
  vendor, e.g. a lone Anthropic key answering for `kimi-k2.5`;
- environment variables were derived from LiteLLM's
`validate_environment`, so
  the key was written into every variable it listed --
`AWS_SECRET_ACCESS_KEY` for Bedrock, `CLOUDFLARE_API_BASE` for
Cloudflare;
- `litellm.api_base` was set process-wide, leaking one provider's
endpoint to
  every other call in the process;
- the OpenRouter shortlist offered bare `anthropic/...` ids, which
auto-detection reads as a request for that vendor direct, quietly
leaving
  OpenRouter as soon as the user also held that vendor's key;
- a `routing.models` entry without an `api_base` fell through to
  `OPENAI_BASE_URL` or `api.openai.com`, shipping the prompt and that
  endpoint's key to a third party; it now raises.

There is no attacker model here -- these are misrouting bugs, not an
authentication bypass -- but each one sends a credential somewhere its
owner did
not intend, so they are called out rather than folded into the summary.

### Also fixed

- an empty declared section no longer answers for a provider, so
credentials
written under one of its other names are not shadowed by a placeholder;
- differently-spelled sections (`azure-openai`, `OpenRouter`, `nanoGpt`)
resolve
to the same provider on both the read and the write path, so a section
written
as LiteLLM spells it is no longer invisible to `provider get/set` and a
write
  no longer leaves two sections for one provider;
- `_litellm_knows` returns three states instead of raising, because only
one of
  its five call sites caught the exception;
- the onboard wizard no longer drops `api_key` / `api_base` for vendors
with no
  registry entry, and no longer overwrites a real key with `None` when a
  re-configure is abandoned;
- `raven status` reads credentials from the loaded config, so ones
supplied by
environment variable are visible, and an OAuth provider without a token
  reports "not set" rather than a checkmark.

### New

`agents.defaults.modelOverrides` lets a user override request params per
model,
which previously only the registry could do. knn-routed models inherit
it, so a
routed model behaves like the default provider.

## Type

- [ ] Fix
- [ ] Feature
- [ ] Docs
- [ ] CI / tooling
- [x] Refactor
- [ ] Other

## Verification

```
uv run --all-extras pytest -q          # 4869 passed, 30 skipped, 0 failed
uv run ruff check raven/ tests/ benchmarks/
uv run ruff format --check raven/ tests/ benchmarks/
```

79 new tests across five files. The provider-resolution suite is
sweep-shaped:
each invariant runs over every registered provider rather than the case
that was
last fixed, since the drift this PR removes was invisible to case-shaped
tests.
Every assertion was checked by breaking the implementation and
confirming it
goes red.

It also carries five source guards against a second implementation
reappearing.
These are line scans -- tripwires, not proofs. A deliberate rewrite
walks past
them; what they catch is the shape that actually recurred, the same
spelling
copied to a new call site.

Behaviour was compared against the base branch over a matrix of provider
configurations, model-id shapes and forced-provider settings. Every
difference
falls into one of the intended changes below.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

Three user-visible behaviour changes:

1. A user whose only credentials are direct vendors -- no gateway -- and
whose
   default model carries another vendor's prefix now gets "provider not
configured" instead of a downstream 401. Anyone holding a gateway key is
   unaffected: a gateway legitimately routes any id.
2. The canonical section names for vLLM and Ollama become `hosted_vllm`
and
`ollama_chat`, LiteLLM's own spellings. Existing configs keep loading
under
   the old names; the next `provider set` consolidates onto the new one.
3. A `routing.models` entry without an `api_base` now raises instead of
   silently defaulting to `api.openai.com`.

Rollback is a revert: no data migration runs, and configs written before
this
change continue to load unchanged.

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

Fixes EverMind-AI#242
Fixes EverMind-AI#243
Fixes EverMind-AI#244
Fixes EverMind-AI#245
Fixes EverMind-AI#246

---------

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
Add a detached BackgroundJobRegistry (setsid, log file, survives agent
exit) next to PTY exec sessions, and route background=true exec calls
to it unconditionally, using a provided session name as the job name.
Session-hosted background processes died with the agent and failed
verifier-time checks in TB eval (4 tasks lost their servers).

Also:
- anchor shutdown/reboot/mkfs deny patterns to command position and
  report the matched pattern in guard errors
- clamp over-limit exec timeouts (configurable tools.exec.maxTimeout)
  instead of rejecting them
- teach session errors the creation path; suggest the matching tool on
  invalid parameters (registry did-you-mean)
- register web_search only when an API key is configured
- spill truncated exec/session output to ~/.raven/tool-output so it can
  be recovered with grep/read_file instead of re-running
- log the final tool manifest (names + schema hash) for eval attribution

Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
Every failure path should carry its own next step (G-1 of the tools
improvement plan):

- repair unambiguous case/format-mangled tool names (Read_File,
  execRead) and execute the intended tool with a note, instead of
  failing the turn
- stop appending the generic change-approach suffix to validation
  errors and tool-authored errors; it buried the targeted guidance
  (did-you-mean, session creation path). Timeouts and unexpected
  exceptions keep it, as they have no better text
- file tools name the recovery tool in not-found errors (list_dir /
  find to locate, write_file to create)

Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
…kpoints

Three long-turn robustness fixes (G-6 of the tools improvement plan):

- refuse a byte-identical (tool, arguments) call after it has hard-failed
  3 times running, instead of executing a fourth identical failure.
  Scoped to all-failed streaks only: identical successful repeats are
  legitimate polling (13 such streaks observed across passing eval
  tasks) and never trip it
- repair dangling tool_call/result pairs in loaded history (synthetic
  aborted results, orphan results dropped) so a crash cannot poison
  later provider calls
- persist the turn-so-far every 10 iterations. A turn is the unit of
  session persistence and benchmark tasks are one turn of hundreds of
  iterations; a crash mid-turn used to lose the whole trajectory

Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
Add agents.defaults.profile (assistant | coding). The default keeps the
personal-assistant identity byte-identical; "coding" renders a
software-engineering identity following opencode's default prompt
structure and content (tone, conventions, no-comments code style,
verify-with-tests discipline, an explicit completion check, and an
opencode-style <env> block), with every tool-specific passage rewritten
for raven's tool surface: find/grep/read_file/edit_file/write_file
routing over shell equivalents, background jobs for servers and
over-ceiling work, sessions + exec_write/exec_read for interactive
programs, and the untrusted-content rule.

ContextBuilder._get_identity now delegates to the segment renderer so
the estimator can no longer drift from the request path.

Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
Attribution analysis of the tb21 full run (raven 39.3% vs terminus
44.9%), the P0/G fixes landed on this branch with their evidence, the
6-task re-verification results (pypi-server and configure-git-webserver
flipped green; kv-store-grpc / install-windows-3.11 now fail on task
logic instead of tool defects), and the deferred items (unified exec,
todo/verification discipline) with measured regression exposure.

Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
Combines the tools-improvement work (detached background jobs, deny
pattern anchoring, timeout clamping, actionable errors, same-call
breaker, mid-turn checkpoints, coding profile) with the raven-x
completion-gate work. The SE-discipline block is hoisted into a shared
constant rendered by BOTH identity profiles, so the eval harness's
profile=coding selection keeps the discipline it was written for.

Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
Resolves the registry boundary against main's ToolOutput/ToolResult
split: the unwrap keeps main's display-text channel while retaining the
tool-name-repair note and the no-generic-hint-on-tool-errors policy;
the loop's test-evidence gate now reads the unwrapped model text.

Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
@TongLi31
TongLi31 merged commit 249a616 into TongLi31:raven-x Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants