dingtalk: drop DINGTALK_TYPE_MAPPING/EXT_MAP re-exports. google_chat: card_spec_to_cards_v2 test -> .cards.
matrix: drop module-level MAX_MESSAGE_LENGTH alias (no importers). teams: drop TeamsSummaryWriter re-export
(teams_pipeline/runtime + tests -> summary_writer). wecom: drop WeComStreamExpiredError/STREAM_EXPIRED_ERRCODE/
MAX_INTERMEDIATE_FRAMES re-exports (tests -> .streaming). parallel: drop _get_parallel_client/_get_async_parallel_client
aliases (tests -> _get_sync_client). email: drop stale 'alias' comment (_esecret_int is the only name).
photon: re-remove credential_summary() (shim-only, cb9b7c36f3); its no-leak test now drives print_credential_summary.
BASE's _compress_codex_app_server_session was the single cache-peek site that read
_agent_cache without a lock when _agent_cache_lock was None (tests/gateway/
test_codex_hygiene_compaction.py::_slash_host relies on it). Every other BASE caller returned
None without a lock. Model that as an explicit lockless_fallback=True at that one call site.
Adolanium review §3 (Medium/Low). BASE shape at every steer/redirect/persist/agent-cache
lock site was: lock = getattr(obj, "_x_lock", None); if lock is not None: with lock: <direct
attribute read>; else: <getattr fallback for object.__new__ test stubs>. The refactor rewrote
several of these as 'with getattr(...) or nullcontext(): getattr(slot, None)', which (a) turned a
missing slot under the lock from a loud AttributeError into silent None and (b) in the gateway
peek helper read the cache without any lock when the lock attribute was absent.
Restored BASE semantics at:
- agent/agent_runtime_helpers.py::_requeue_pending_steer
- agent/interrupt_control.py: steer, redirect, clear_interrupt, _has_pending_redirect,
_drain_pending_redirect, _drain_pending_steer (new _ic_slot helper: direct read under lock)
- agent/session_persistence.py::_persist_lock (explicit None check + BASE rationale)
- gateway/slash_commands.py::_cached_agent_for (BASE callers read ONLY under the lock; no lock -> None)
- gateway/run_agent_cache.py::_evict_cached_agent (BASE: self._agent_cache direct under lock)
- agent/client_lifecycle.py::_is_openai_client_closed: BASE body + docstring verbatim (outer
is_closed first; inner _client.is_closed only when _client exists; else False)
- agent/stream_delivery.py::_ensure_stream_writer_state: restore BASE rationale that the lock is
created unconditionally in agent_init (_STREAM_STATE) and the lazy path is stub-only
A/B (/tmp/rf/rev/ab_lock_fallbacks.py, ab_client_closed.py) is byte-identical BASE vs HEAD on
the reviewer's inputs + edge cases. tests/agent/test_lock_fallback_base_semantics.py pins it
(7 of 25 cases fail on the pre-fix tree).
The static py-modules list in pyproject.toml drifted from the source
tree each time the root layout changed. An installed wheel then
raised ModuleNotFoundError on import: hermes_state_common, and every
gateway or CLI start failed. The list missed hermes_state_holders,
mini_swe_runner, and the 15 new hermes_state_* modules from this
branch.
setup.py now derives py_modules from the source tree at build time.
setuptools package discovery sees only directories with an
__init__.py, so root single-file modules need py_modules in every
wheel build. setup() kwargs merge with pyproject.toml, and setup.py
is the only legitimate wheel or sdist builder, so the derived list is
the single source of truth.
The nix build is the only wheel consumer. Its source filter keeps
every root .py file, so the build sandbox derives the same set as the
checkout. Editable installs do not read py_modules: build_editable
never runs bdist_wheel.
Verified: wheel and sdist built with HERMES_NIX_BUILD=1 carry all 38
root modules. The guard still blocks builds without the nix env var.
The sealed uv2nix venv from nix build .#default imports
hermes_state_holders, hermes_state_sessions, mini_swe_runner, and
hermes_startup_watchdog.
(cherry picked from commit e23d467b52)
Invariant test updated to pin the derived list (no static py-modules; every root module packaged code imports ships).
On BASE the #95514 stream-recovery rebound final_response inline, before
the tail-closing / persist-override / micro-compaction / _persist_session
calls inside the guarded persist try-block, so a raise in any of them left
the caller with the streamed text (cleanup_errors reported the failure).
HEAD's _close_transcript_tail returned the recovered value only on normal
completion; an exception in the same block dropped it and the user-visible
answer regressed to the pre-recovery '' (then rewritten by the explainer).
Split the helper into _drop_transcript_scaffolding / _recover_final_from_stream
/ _close_transcript_tail and rebind final_response in the guarded step as soon
as it is computed, restoring BASE ordering. Adds a regression test.
ethernet8023: the seeded E2E (turn.start -> deltas -> turn.end with history_version/
message_count) was cut to hello+bogus-frame while ComputeHost._run_real_turn stays live.
New suite drives server._sessions + _run_prompt_submit through the host: turn.started,
message.delta rpc frames, turn.end identity (history_version=1, message_count=2), sid-
required/session-busy turn.error, stale queued-generation -> interrupted, live interrupt
frame ack + interrupted turn.end, and the explicit-stop compat matrix ported off HostSession.
Follow-up to b8f99bfc43: the seam edits for hermes_state_repair.py and tools/approval_detection.py
were overwritten by a concurrent squad's write before that commit landed (only their docstring
restores got in). Re-apply: _repair_conn/_open_exclusive/_db_opens_cleanly look up
_connect_repair_durable via hermes_state at call time; detect_dangerous_command/
detect_hardline_command look up _command_detection_variants via tools.approval, so patching the
facade (as tests/state/test_state_db_wal_unlink_race.py and tests/hermes_cli/test_approvals_test.py
do) reaches the call again, as on BASE 63279301bc.
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
Reviewer P2 (kshitijk4poor): tests patch hermes_cli.models.get_cached_nous_inference_base_url
but models_pricing.pricing_cache_scope read its own module global, so the patch never reached
the call and the test passed on the default-endpoint fallback. Same seam-erosion class audited
across /tmp/rf/patch_traps.json (777 candidates) with an AST reachability check + a per-test
call-count probe (facade vs defining module) against BASE 63279301bcb; three seams actually
bypassed their patch on HEAD but not on BASE:
- hermes_cli.models.get_cached_nous_inference_base_url <- models_pricing.pricing_cache_scope
- hermes_state._connect_repair_durable <- hermes_state_repair._open_exclusive/_repair_conn/_db_opens_cleanly
- tools.approval._command_detection_variants <- approval_detection.detect_{dangerous,hardline}_command
Each now looks the name up through its facade at call time (the pattern hermes_state_repair
already used in live_writer_holds_db), restoring BASE's patchability.
All public on BASE 63279301bc, dropped by the simplify refactor (their tests were deleted or
rewritten to the replacement API). Restore each with BASE signature/body as a thin wrapper over the
surviving implementation, and restore the tests at the original call sites: test_message_reactions
again asserts the role=user contract (a newer assistant message is never the default target);
test_hermes_state / test_watchdog_review_76354 go back to get_session_activity(); toolsets, acp auth,
edit_approval, billing-scope and curated-models tests restored/extended.
ethernet8023: IAM streaming-denial -> converse fallback and stale-ConnectionClosedError
client eviction lost all coverage. Both public helpers restored byte-identical to BASE
(with THROTTLE/OVERLOAD/CONTEXT_OVERFLOW patterns + is_context_overflow_error), the 4
tests re-added verbatim, and TestAgentBedrockStreamRecovery covers the path the agent
actually uses (chat_completion_helpers._bedrock_converse_call / _BedrockStream._fall_back_to_converse).
ethernet8023: TestValidateImageUrl (incl. localhost block) was deleted with the sync
validator. Both names restored with BASE semantics; the async validator (the live
download path) now shares _image_url_shape_ok and gets its own localhost/malformed test.
ethernet8023: the no-leak test was deleted alongside the function it pinned; the
display-only credential status is live in print_credential_summary. credential_summary
restored byte-identical to BASE so the leak contract is CI-pinned again.
ethernet8023: both documented VAD false-trip regression tests were deleted with the
function they covered. listen_for_speech is public API on main (plugins may import it),
so the body is restored byte-identical to BASE; full_duplex_listen/_BargeDetector is
A/B-verified identical to BASE over 400 fuzzed playback/speech scenarios.
BASE (63279301bc) exported agent.agent_init._moa_reference_output_allowed and
_relay_moa_reference_event (salvaged in 3dfe712384 from #67334, plugin-importable,
zero in-tree callers) and covered them with
tests/agent/test_moa_quiet_reference_output.py. The simplification PR deleted both
the helpers and the test. Restore them byte-identical to BASE.
Note: the live build_moa_facade relay in agent/moa_loop.py is unchanged. A/B
harness (/tmp/rf/rev/moa_quiet_ab.py) shows BASE's facade relay already fired
moa.reference under platform=cli/tool_progress_mode=off — the guard only ever
lived in the orphan helper. -Q is protected on both trees by cli.py nulling
agent.tool_progress_callback (_configure_quiet_agent / BASE cli.py:22308).
BASE exposed these public names; the simplify refactor dropped them (and deleted/removed their
tests) although plugins/connectors import them. Restore each with BASE's signature and body
(download() again routes its auth decision through is_relay_media_url), and restore the covering
tests ported to the new layout, plus DB-only/task-cwd coverage for remove_session/cleanup, the
zero->4096 chunking normalization for from_platform_entry, and len() on empty/populated registries.
A/B vs 63279301bc: /tmp/rf/rev/ab_publicapi.py identical output on both trees.
main's 'and len(current_lines) > 1' fence-close guard is dead (the opening
fence is always already in current_lines), so dropping it is byte-identical
for every input (A/B: 11 empty/nested fence shapes). Add a parity test that
asserts main's atoms for empty ``` blocks so the boundary stays pinned.
main guarded scroll's x/y independently (coordinate=[null,100] -> y=100),
while click treats a coordinate without x as no point. The shared _xy()
applied click semantics to scroll; split out _scroll_xy() with main's
per-axis guard and pin both behaviors in a test.
`_write_schema_cache` in the extracted owner tools/mcp_tool_registration.py
read `t.inputSchema` with a bare camelCase getattr. mcp 2.0 renamed the
model field to `input_schema` and kept `inputSchema` only as a serialization
alias, which pydantic does not apply to attribute access, so on SDK 2.x the
cache persisted `"inputSchema": {}` for every tool and a `lazy: true` server
registered from that cache was advertised to the model with all parameters
stripped. Pre-existing on BASE (tools/mcp_tool.py); fixed with the canonical
`mcp_field(t, "input_schema", "inputSchema")` helper, as both PRs do.
Adds the real-SDK regression test from #102129 (genuine `mcp.types.Tool`
driven through `_register_server_tools` -> cache write ->
`_register_from_cache_sync`), ported to the new module layout and isolated
from the module-global registration state.
Co-authored-by: mzkarami <mehrzad.karami@gmail.com>
Co-authored-by: lijinxiao1982 <120761624+lijinxiao1982@users.noreply.github.com>
The hermes_state split added 15 root-level sibling modules but
[tool.setuptools] py-modules still listed only the old set, so the built
wheel/sdist (and the uv2nix sealed venv) failed at 'import hermes_state'
with ModuleNotFoundError. hermes_state_holders and hermes_state_registry
were already missing on main (registry is imported by gateway/run.py and
hermes_state.py).
Add an invariant test that parses pyproject and walks every import in the
packaged root modules and packages, requiring any import that resolves to
a root-level *.py to be declared - so the class of bug is caught by CI
rather than by a broken install.