Skip to content

ByteRover V4 - #1

Open
cuongdo-byterover wants to merge 8 commits into
mainfrom
feat/byterover-mono
Open

ByteRover V4#1
cuongdo-byterover wants to merge 8 commits into
mainfrom
feat/byterover-mono

Conversation

@cuongdo-byterover

@cuongdo-byterover cuongdo-byterover commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

This PR updates the Hermes byterover memory provider to use the ByteRover mono skill runtime instead of the deprecated brv CLI flow.

The provider now runs ByteRover mono's bundled Node scripts as subprocesses, keeps recall automatic through prefetch(), and exposes one agent-facing write tool: brv_record for structured <bv-topic> HTML records.

What Changed

  • Replaced the old CLI-backed provider flow with recall.mjs and record.mjs from the installed ByteRover skill.
  • Added Hermes-native script discovery:
    • BYTEROVER_MONO_SCRIPTS_DIR when explicitly set.
    • $HERMES_HOME/skills/byterover/scripts for normal Hermes skill installs.
  • Updated the runtime guidance so the agent follows the current ByteRover skill contract:
    • requires <bv-reason> in authored topics,
    • uses rich --html topic writes,
    • includes disclosure guidance,
    • removes the obsolete brv.mjs init path.
  • Kept recall best-effort and automatic through prefetch(query), returning a <byterover-context> block when mono finds relevant context.
  • Replaced the old write/query/status tool set with a single brv_record(path, html, overwrite=False) tool.
  • Updated plugins/memory/byterover/README.md and plugin.yaml for the mono distribution model.
  • Added the release author map entry for cuong@byterover.dev so contributor attribution CI passes.

Breaking Changes

  • brv_query, brv_curate, and brv_status are removed.
  • BRV_API_KEY is no longer part of this provider's config surface.
  • Automatic curation hooks are removed; curation is now explicitly agent-tool-driven through brv_record.
  • Node.js and the ByteRover skill scripts must be available at runtime.

Testing

Local checks:

python3 -m py_compile plugins/memory/byterover/__init__.py scripts/release.py
git diff --check origin/main...HEAD
uv run --extra dev pytest tests/agent/test_memory_provider.py -q

Result: 91 passed for the focused memory-provider test suite.

PR CI is green:

  • contributor attribution
  • supply-chain checks
  • docs-site checks
  • ruff and ty
  • typecheck
  • full test matrix
  • OSV scanner
  • Docker and shell lint

Notes For Reviewers

The provider intentionally does not create or bind ByteRover spaces. ByteRover mono resolves storage through its own space registry from the subprocess working directory, with Hermes using $HERMES_HOME/byterover as the stable workspace identity.

cuongdo-byterover pushed a commit that referenced this pull request Jun 18, 2026
…ch#37677)

Anthropic enforces two independent ceilings per image:
1. 5 MB encoded byte size
2. 8000 px longest side

Hermes only guarded #1. A tall screenshot (e.g. 1200x12000 at 0.06 MB)
passes every byte check but fails the pixel check, returning a
non-retryable HTTP 400 that permanently bricks the conversation thread.

Fixes:
- error_classifier: add 'image dimensions exceed' pattern to
  _IMAGE_TOO_LARGE_PATTERNS so the 400 is classified as image_too_large
  and triggers the shrink/retry path instead of falling through to
  non-retryable error.
- conversation_compression: check pixel dimensions (via Pillow) even
  when byte size is under the 4 MB target. If max(dims) > 8000, force
  shrink.
- vision_tools._resize_image_for_vision: add optional max_dimension param.
  When set, images exceeding the pixel cap are downscaled even if they're
  under the byte budget. The resize loop now checks both byte AND pixel
  limits before accepting a candidate.

Closes NousResearch#37677
cuongdo-byterover pushed a commit that referenced this pull request Jun 18, 2026
…bes + test-leak fix (NousResearch#40909)

* fix(gateway,windows): reliability — supervisor task, JOB breakaway, status --deep

Three coordinated fixes for the Windows gateway reliability story:

1. CREATE_BREAKAWAY_FROM_JOB on every detached spawn

   The 'hermes update' triggered from the Electron Desktop GUI ran inside
   Electron's job object. Without breakaway, the post-update gateway
   watcher spawned by update — already DETACHED_PROCESS — was still
   reaped when Electron's job tore down, so the gateway never came back
   after a GUI-initiated update. Adds CREATE_BREAKAWAY_FROM_JOB (0x01000000)
   to:
     - hermes_cli/_subprocess_compat.py::windows_detach_flags() — used by
       every helper that calls windows_detach_popen_kwargs(), including
       launch_detached_profile_gateway_restart()
     - The watcher subprocess's own respawn snippet in
       hermes_cli/gateway.py (inlined flags so the watcher's child
       respawn also breaks away)

   _spawn_detached() in gateway_windows.py already had the flag; this
   change brings the rest of the codebase to parity.

2. Per-minute supervisor Scheduled Task — Windows equivalent of
   systemd Restart=always

   Introduces hermes_cli/gateway_supervisor.py and registers it as a
   second Scheduled Task ('Hermes_Gateway_Supervisor', SC MINUTE /MO 1,
   LIMITED rights) alongside the existing ONLOGON task. Every minute,
   the supervisor uses the same gateway.status.get_running_pid() probe
   as 'hermes gateway status' and, if no gateway is alive, calls
   gateway_windows._spawn_detached() (which now includes BREAKAWAY) to
   bring one back.

   Covers every crash mode, not just 'machine rebooted': taskkill,
   OOM, GUI update SIGTERM, parent job teardown. Cheap — one pythonw
   startup per minute when down, one PID-existence check per minute
   when up.

   Wired into both the schtasks-success and Startup-folder-fallback
   install paths via _install_supervisor_best_effort(), and removed in
   uninstall(). Best-effort: a failing supervisor install logs a
   warning but doesn't roll back the primary install.

3. 'hermes gateway status --deep' shows per-probe PASS/FAIL

   Replaces the existing terse '--deep' output (which only printed
   paths) with an actual diagnostic table:
     [1] PID file present
     [2] Lock file held by a live process
     [3] get_running_pid() result
     [4] _pid_exists(pid) — OS-level liveness
     [5] gateway_state.json (state + age)
     [6] Last lifecycle event from gateway-exit-diag.log

   When the high-level summary disagrees with reality, the user can
   see exactly which signal is lying.

Test-leak fix
-------------

tests/hermes_cli/test_gateway_wsl.py::TestGatewayCommandWSLMessages
monkey-patched is_linux/is_wsl/supports_systemd_services to simulate
WSL but did NOT stub is_windows(). On a Windows host, the dispatcher
in _gateway_command_inner takes the is_windows() branch BEFORE the
WSL guidance branch, so the test invoked gateway_windows.install()
for real. install() writes to %APPDATA%\...\Startup\Hermes_Gateway.cmd
— the REAL user Startup folder, never sandboxed by tmp_path — pointing
at the test's pytest-of-<user>/pytest-<N>/.../gateway-service/ wrapper.
When pytest tore down the tmp_path, every subsequent Windows login
flashed a cmd.exe window that failed to find the missing target.

Stubs is_windows=False on all four affected tests:
  test_install_wsl_no_systemd
  test_start_wsl_no_systemd
  test_status_wsl_running_manual
  test_status_wsl_not_running

Defense-in-depth: _build_startup_launcher() now prefixes the launcher
with 'if not exist <target> exit /b 0', so any future stale Startup
entry silently no-ops instead of flashing a console window.

Status enhancements
-------------------

- status() now reports supervisor task presence alongside the existing
  schtasks/Startup info, and nudges the user to reinstall if the
  supervisor isn't registered.
- Deep mode dumps both the supervisor task name + script path.

* fix(gateway,windows): drop the per-minute supervisor task — keep breakaway + deep probes

Earlier in this branch we added a per-minute schtasks-based supervisor to
respawn the gateway after crashes / GUI-update SIGTERMs. The implementation
flashed a brief console window on every firing, which stole window focus.
We tried several variants:

  - cmd.exe wrapper invoking pythonw  -> flashes (cmd.exe is console-subsystem)
  - schtasks /TR pointing at pythonw  -> flashes (uv venv launcher pythonw is
    actually subsystem=Console, not GUI; it respawns the real pythonw)
  - schtasks /TR pointing at base uv  -> still flashes (Task Scheduler-side
    conhost preallocation; documented Windows quirk)
  - XML registration with <Hidden>true>  -> still flashes (<Hidden> only hides
    the task in the Task Scheduler UI, not the spawned window)

Researched what leading projects do:

  - Ollama: GUI-subsystem tray exe + Startup-folder shortcut. No supervisor.
  - Tailscale: real Windows Service via SCM. Session 0, no console possible.
  - Syncthing: --no-console flag inside the binary + Startup folder.
  - openclaw: VBS Run(..., 0, False) wrapper. Suppresses the *window* but
    Super User Q971162 confirms focus-steal still occurs in some cases.

None of these use a per-minute polling scheduled task. The 'auto-restart on
crash' responsibility belongs INSIDE the daemon (Tailscale's in-process
recovery / Ollama's monitor+worker pair) OR is delegated to the Windows
Service Control Manager — not Task Scheduler.

So this commit drops the supervisor entirely. The CREATE_BREAKAWAY_FROM_JOB
fix in _subprocess_compat.py (from commit c1e5fa4) survives — that is the
*real* fix for problem NousResearch#2 (GUI-update kills gateway): the post-update
watcher in launch_detached_profile_gateway_restart() now breaks out of
Electron's job object, so the gateway respawn watcher survives the GUI
quit and successfully respawns the gateway.

Surviving from c1e5fa4:
  * CREATE_BREAKAWAY_FROM_JOB in hermes_cli/_subprocess_compat.py (fixes NousResearch#2)
  * Inlined breakaway flag in the watcher respawn snippet in gateway.py
  * hermes gateway status --deep PASS/FAIL probes (fixes #1 — visibility)
  * 'if not exist <target> exit /b 0' guard in _build_startup_launcher
    (fixes NousResearch#3 — silent no-op for stale Startup entries)
  * tests/hermes_cli/test_gateway_wsl.py is_windows=False stubs (root cause
    of NousResearch#3 — pytest WSL tests no longer leak Startup entries on Win hosts)

Removed in this commit:
  * hermes_cli/gateway_supervisor.py (entire file)
  * Supervisor section in hermes_cli/gateway_windows.py (~180 lines):
      get_supervisor_task_name, get_supervisor_script_path,
      _build_supervisor_cmd_script, _write_supervisor_script,
      _install_supervisor_task, is_supervisor_task_registered,
      _install_supervisor_best_effort
  * _install_supervisor_best_effort() calls in install() (3 spots)
  * supervisor cleanup block in uninstall()
  * supervisor display lines in status() / status(deep=True)

Future direction (out of scope for this PR): the right place for Windows
'Restart=always' semantics is a real Windows Service installed via
pywin32's win32serviceutil.ServiceFramework — session-0 isolation, SCM
auto-restart, no console window possible. That's a meaningful next-PR
project, not a band-aid.

Tests: 51 pass / 2 pre-existing failures in
tests/hermes_cli/test_gateway_{windows,wsl}.py (the 2 failures are
TestSupportsSystemdServicesWSL cases that fail on origin/main too —
unrelated to this PR).
cuongdo-byterover pushed a commit that referenced this pull request Jun 18, 2026
Add an official, production-grade WhatsApp integration via Meta's
Business Cloud API as a complement to the existing Baileys bridge.
No bridge subprocess, no QR codes, no account-ban risk — at the cost
of a Meta Business account and a public HTTPS webhook URL.

Setup is fully wizard-driven: 'hermes whatsapp-cloud' walks through
every credential with paste-time validation (catches the #1 trap of
pasting a phone number into the Phone Number ID field), generates a
verify token, and ends with copy-paste instructions for the
cloudflared / Meta-dashboard / Business Manager pieces that can't be
automated. The wizard also points users at Meta's Business Manager
for setting the bot's display name and profile picture.

Feature set:

- Inbound: text, images (with native-vision routing), voice notes
  (STT), documents (small text inlined, larger cached), reply context.
- Outbound: text with WhatsApp-flavored markdown conversion, images,
  videos, documents, opus voice notes via ffmpeg with MP3 fallback.
- Native interactive buttons for clarify, dangerous-command approval,
  and slash-command confirmation flows — matches the Telegram /
  Discord UX, graceful degrades to plain text.
- Read receipts (blue double-checkmarks) and typing indicator,
  using Meta's combined endpoint so they fire in a single API call.
- Webhook security: X-Hub-Signature-256 HMAC verification (raw body,
  constant-time), wamid deduplication, group-shaped-message refusal
  (groups deferred to v2 — Baileys still covers them).
- Full integration with the gateway's session, cron, display-tier,
  prompt-hint, and auth-allowlist systems. Cloud and Baileys can run
  side-by-side against different phone numbers.

Also wires STT (speech-to-text) through Nous's managed audio gateway
for Nous subscribers — previously the default stt.provider=local
required a separate faster-whisper install. New subscribers now get
voice-note transcription out of the box.

Docs: 418-line user guide at website/docs/user-guide/messaging/
whatsapp-cloud.md, sidebar entry, environment-variables reference,
ADDING_A_PLATFORM.md updated with the optional interactive-UX
contract for future adapter authors.

Tests: 100 dedicated tests for the adapter, 32 for the setup wizard,
20 for the Nous subscription STT wiring, plus regression coverage
across display_config, prompt_builder, and the cron scheduler.

Known limitations (deferred until clear demand signal):
- Group chats — use the Baileys bridge if you need them.
- Message templates for 24-hour-window outside-conversation sends —
  reactive chat is unaffected; cron / delegate_task with gaps > 24h
  will fail with a clear error. The agent's system prompt warns the
  model about this so it knows to mention it when scheduling delayed
  messages.
@github-actions

Copy link
Copy Markdown

⚠️ MCP catalog security review required

This PR changes the bundled MCP catalog or MCP catalog installer code. MCP entries can define local commands that users later install into mcp_servers, so this needs explicit maintainer review before merge.

A maintainer should verify:

  • any new/changed optional-mcps/**/manifest.yaml command and args are expected,
  • stdio transports do not use shell+egress/exfiltration payloads,
  • git install refs are pinned and bootstrap commands are minimal,
  • requested env vars/secrets match the upstream MCP's documented needs.

After review, add the mcp-catalog-reviewed label and re-run this check.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown

🔎 Lint report: feat/byterover-mono vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 11008 on HEAD, 11009 on base (✅ -1)

🆕 New issues (2):

Rule Count
unresolved-attribute 2
First entries
run_agent.py:2941: [unresolved-attribute] unresolved-attribute: Object of type `Self@get_credits_spent_micros` has no attribute `_credits_session_start_micros`
tests/run_agent/test_credits_notices_toggle.py:76: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_credits_session_start_micros` on type `AIAgent`

✅ Fixed issues (4):

Rule Count
invalid-method-override 2
invalid-parameter-default 1
invalid-assignment 1
First entries
plugins/memory/byterover/__init__.py:79: [invalid-parameter-default] invalid-parameter-default: Default value of type `None` is not assignable to annotated parameter type `str`
plugins/memory/byterover/__init__.py:238: [invalid-method-override] invalid-method-override: Invalid override of method `sync_turn`: Definition is incompatible with `MemoryProvider.sync_turn`
tests/run_agent/test_credits_notices_toggle.py:76: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to attribute `_credits_session_start_micros` of type `int`
plugins/memory/byterover/__init__.py:265: [invalid-method-override] invalid-method-override: Invalid override of method `on_memory_write`: Definition is incompatible with `MemoryProvider.on_memory_write`

Unchanged: 5765 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@github-actions

Copy link
Copy Markdown

⚠️ MCP catalog security review required

This PR changes the bundled MCP catalog or MCP catalog installer code. MCP entries can define local commands that users later install into mcp_servers, so this needs explicit maintainer review before merge.

A maintainer should verify:

  • any new/changed optional-mcps/**/manifest.yaml command and args are expected,
  • stdio transports do not use shell+egress/exfiltration payloads,
  • git install refs are pinned and bootstrap commands are minimal,
  • requested env vars/secrets match the upstream MCP's documented needs.

After review, add the mcp-catalog-reviewed label and re-run this check.

1 similar comment
@github-actions

Copy link
Copy Markdown

⚠️ MCP catalog security review required

This PR changes the bundled MCP catalog or MCP catalog installer code. MCP entries can define local commands that users later install into mcp_servers, so this needs explicit maintainer review before merge.

A maintainer should verify:

  • any new/changed optional-mcps/**/manifest.yaml command and args are expected,
  • stdio transports do not use shell+egress/exfiltration payloads,
  • git install refs are pinned and bootstrap commands are minimal,
  • requested env vars/secrets match the upstream MCP's documented needs.

After review, add the mcp-catalog-reviewed label and re-run this check.

… scripts

Replaces the v1 cli-binary subprocess plugin with a byterover-mono direct
build. Same name + plugin discovery surface so Hermes' loader picks it up
without config changes; only the backend swaps.

Architectural diff vs v1:

  Before (v1.0.0)
  ───────────────
    _resolve_brv_path() → finds `brv` binary on PATH
    _run_brv(args, cwd) → subprocess.run([brv_path] + args, …)
    Tools exposed:
      brv_query   → `brv query <q>`
      brv_curate  → `brv curate <natural-language>` (multi-step session)
      brv_status  → `brv status`
    Hook: on_pre_compress → background `brv curate` auto-flush
    Auto-curate via: sync_turn() and on_memory_write() threads
    External dep: brv CLI binary
    Config: BRV_API_KEY env var

  After (v2.0.0-mono.0)
  ─────────────────────
    _resolve_scripts_dir() → finds mono's scripts/ folder
       precedence:
         1. $BYTEROVER_MONO_SCRIPTS_DIR env var
         2. ~/.openclaw/skills/byterover/scripts/  (if openclaw installed)
         3. ~/workspaces/byterover-mono/skills/byterover/scripts/  (dev)
    _run_mono(script, args, cwd) → subprocess.run([node, script, …], …)
    Tools exposed:
      brv_record  → `node record.mjs <path> --html '<bv-topic …>…</bv-topic>'`
    Hooks: NONE (dropped on_pre_compress — auto-flush was guessing what to
    save from raw message text; the agent picks better via brv_record)
    Auto-curate: REMOVED (sync_turn, on_memory_write are no-ops)
    External dep: node (Node.js runtime)
    Config: no API key, no auth — mono is local-first

The provider implements:
  - is_available(): True iff node + scripts dir both resolve
  - initialize(session, **kwargs): chdirs into $HERMES_HOME/byterover/
    and runs `brv.mjs init` (idempotent) so mono creates the central
    tree at ~/.brv/projects/<flat-of-cwd>/context-tree/
  - system_prompt_block(): returns the full ~11KB curate guidance every
    turn (per integration plan Q2 — no smart-debounce). Includes the
    IRON LAW preamble, 19-element <bv-*> vocabulary reference, structural
    rules, brv_record tool contract, and the worked example showing rich
    vs flat shape.
  - prefetch(query): spawns recall.mjs and returns rendered topics
    wrapped in a <byterover-context> block with directive instructions
    (READ / ALIGN / CITE / SUPPLEMENT / FLAG). Best-effort: any failure
    returns "" so a recall outage cannot block the agent loop.
  - handle_tool_call(brv_record, args): shells record.mjs with --html.
    Returns the JSON envelope from record.mjs verbatim.
  - shutdown(): no-op (no daemon, no threads, no resources).

Storage location: still $HERMES_HOME/byterover/ from Hermes' view, but
mono's resolveContextRoot maps that cwd to
~/.brv/projects/<flat-of-cwd>/context-tree/ — centralized, joinable with
other byterover-mono installs across the system, not duplicated per
Hermes profile.

Cost: ~2,128 tokens per turn for the system prompt block (curate
guidance). Flat overhead — doesn't scale with conversation length.

Deps update in plugin.yaml: removed `brv`, added `node`, dropped the
on_pre_compress hook declaration. Bumped version to 2.0.0-mono.0 to
signal the major behavior break.

README rewritten with install instructions, the three discovery
fallback paths, the agent-tool contract, and a v1 → v2 migration table.

CROSS-REPO NOTE: This plugin depends on byterover-mono's recall.mjs
entry, which lives on commit 4c3c6f4 (branch feat/openclaw-recall-entry).
That commit is not yet merged into byterover-mono main. To use this
plugin, that recall.mjs must be present at the discovered scripts dir
(either by merging the feat branch, by checking it out, or by running
`pnpm build:skill` from a worktree that has it).
Completes the v2.0.0-mono.0 cutover started in 2bf16f3df. The earlier
commit only landed __init__.py; plugin.yaml and README.md got left at
their v1 content. This commit reconciles them.

plugin.yaml
  - version: 1.0.0 → 2.0.0-mono.0
  - description: updated to reflect mono backend + brv_record tool
  - external_dependencies: brv binary → node runtime
  - hooks: dropped on_pre_compress (no auto-flush in the mono build —
    curation is agent-tool-driven via brv_record)

README.md
  - install instructions for the three scripts-dir fallback paths
  - tool contract for brv_record (path, html, overwrite)
  - v1 → v2 migration table
  - storage-location note (centralized at ~/.brv/projects/<flat>/, not
    inside $HERMES_HOME)
@cuongdo-byterover cuongdo-byterover changed the title feat(byterover): switch to byterover-mono backend (v2.0.0-mono.0) Byterover V4 Jun 18, 2026
@cuongdo-byterover cuongdo-byterover changed the title Byterover V4 ByteRover V4 Jun 18, 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.

1 participant