feat(context-engine): add ByteRover context engine plugin#43920
feat(context-engine): add ByteRover context engine plugin#43920danhdoan wants to merge 6 commits into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ba829ff7ab
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const assembleTimeout = this.config.queryTimeoutMs | ||
| ? Math.min(this.config.queryTimeoutMs, 10_000) | ||
| : 10_000; |
There was a problem hiding this comment.
Honor configured query timeout in assemble
The assemble path clamps queryTimeoutMs to 10s (Math.min(..., 10_000)), so any configured value above 10,000 ms and the documented default behavior in brv-process (12,000 ms) are silently ignored. In slow environments this causes avoidable aborts and skipped context injection even when operators explicitly raise the timeout, making plugin behavior inconsistent with its config contract.
Useful? React with 👍 / 👎.
| child.stderr.on("data", (chunk: Buffer) => { | ||
| stderr += chunk.toString("utf8"); |
There was a problem hiding this comment.
Bound stderr buffering in brv process runner
Only stdout is size-limited, but stderr is appended without any cap, so a noisy or failing brv invocation can still accumulate unbounded output in memory and defeat the intended output-safety guard. This can lead to unnecessary memory pressure/OOM during error scenarios, especially since this path runs on every turn for curation/query.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR introduces the ByteRover context engine plugin ( Two issues worth addressing before merge:
Confidence Score: 4/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/byterover/brv-process.ts
Line: 129-131
Comment:
**Unbounded `stderr` accumulation**
`stdout` is correctly capped at `maxOutput` (512 KB by default), but `stderr` has no size limit. A misbehaving or unexpectedly verbose `brv` process (e.g., verbose logging mode, crash dump, or a corrupted install) can grow `stderr` without bound in the gateway process's heap, creating the same memory-exhaustion vector that `maxOutput` was introduced to prevent.
```suggestion
child.stderr.on("data", (chunk: Buffer) => {
stderr += chunk.toString("utf8");
if (stderr.length > maxOutput) {
stderr = stderr.slice(0, maxOutput) + "\n[stderr truncated]";
}
});
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/byterover/context-engine.ts
Line: 145-147
Comment:
**`queryTimeoutMs` silently capped at 10 s for assembly**
The `assembleTimeout` is `Math.min(queryTimeoutMs, 10_000)`, so a user who sets `queryTimeoutMs: 20000` in their config will observe a hard 10 s ceiling with no warning. Meanwhile `runBrv` receives the full configured value as its internal timer — meaning the AbortController always wins first, making the configured value effectively useless above 10 000 ms.
The `openclaw.plugin.json` schema describes `queryTimeoutMs` as "Timeout in milliseconds for brv query calls" with no mention of this cap, so users will reasonably expect their setting to be honoured.
Two options:
1. **Document the cap in the schema description** (minimal change):
```json
"queryTimeoutMs": {
"type": "number",
"description": "Timeout for brv query calls (ms). Defaults to 12000. Note: effective assemble deadline is capped at 10000 ms to stay within the agent ready timeout."
}
```
2. **Expose the cap as a separate config field** (`assembleQueryTimeoutMs`) so the two knobs are independent and both user-controlled.
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: ba829ff |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b57679057c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const AGENT_TAG_RE = /<\s*\/?\s*(?:final|think)\s*>/gi; | ||
|
|
||
| export function stripAssistantTags(text: string): string { | ||
| if (!text) return text; | ||
| return text.replace(AGENT_TAG_RE, ""); |
There was a problem hiding this comment.
Strip block contents before curation
stripAssistantTags removes only the <think>/<final> delimiters, so assistant messages like <think>internal reasoning</think><final>answer</final> are transformed into internal reasoninganswer and the hidden reasoning is sent to brv curate. This means non-final chain-of-thought text can be persisted and later re-injected via systemPromptAddition, which contaminates retrieved context and can surface speculative/unsafe internal reasoning in future turns.
Useful? React with 👍 / 👎.
|
can't wait for this |
|
This PR significantly improves how the agent manages context, let's get this merged. |
Thanks for your interest, @asyncjason! ByteRover is now supporting 4 plans as below: In short: I suggest you first try Pro plan to explore and experience full functionalities. You would love it! |
Add optional `prompt` field to the ContextEngine `assemble` interface so context engines can prepare relevant context based on the current user message rather than only historical messages.
Integrates the brv CLI as an OpenClaw context engine that curates conversation turns and queries curated knowledge for prompt injection. - assemble: queries brv for relevant context, injects as systemPromptAddition - afterTurn: feeds new turn messages to brv curate (fire-and-forget) - compact: delegates to runtime (brv daemon manages its own compaction) - Uses api.logger throughout, ENOENT gives actionable install guidance - Arg terminator (--) prevents user text from being parsed as brv flags
scoped plugin-sdk subpath - afterTurn: prepend curation instructions that tell brv to skip trivial messages (greetings, acks, session-start commands) server-side - assemble: skip brv query when stripped query is < 5 chars (avoids spawning for "ok", "hi", "yes" — applied after metadata stripping) - Migrate imports from monolithic openclaw/plugin-sdk to scoped openclaw/plugin-sdk/byterover subpath (new src/plugin-sdk/byterover.ts) - Wire export map entry in package.json and register in subpaths test
… integration tests - Add settled guard in runBrv to prevent double resolve/reject from competing event handlers (timer, signal, overflow, error, close) - Replace Promise.race timeout in assemble with AbortController signal passed through brvQuery → runBrv for clean child-process cancellation - Expand detach comment to clarify brv daemon handshake behavior - Remove dead code: brvStatus function and BrvStatusResult type - Add 12 integration tests covering afterTurn→brvCurate, assemble→brvQuery, full lifecycle, abort handling, and error fallback paths
…t ceiling - Truncate stderr at maxOutput (512KB) to prevent unbounded memory growth from verbose or crashing brv processes - Document the 10s assemble deadline cap in queryTimeoutMs schema description so users understand why values above 10000 ms are not honored
- Add "byterover" to pluginSdkSubpaths in vitest.config.ts so the openclaw/plugin-sdk/byterover import resolves without dist/ on CI - Use typeof import() for vi.fn generics to properly type mock params
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78e0d8a191
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const maxOutput = params.maxOutputChars ?? 512_000; | ||
|
|
||
| params.logger.debug?.( | ||
| `spawn: ${params.brvPath} ${params.args.join(" ")} (cwd=${params.cwd}, timeout=${params.timeoutMs}ms)`, |
There was a problem hiding this comment.
Redact conversation text from spawn debug logs
runBrv currently logs params.args.join(" "), which includes raw user query text and full serialized conversation payloads for brv curate; when debug logging is enabled, this writes sensitive conversation content (and potentially very large blobs) into logs on every turn. That creates avoidable data-leak and log-growth risk in real deployments, so this log line should avoid printing user-supplied args verbatim (for example, log subcommand plus payload length only).
Useful? React with 👍 / 👎.
| } | ||
| } | ||
| // "--" terminates flags so user text starting with "-" isn't parsed as a brv option | ||
| args.push("--", params.context); |
There was a problem hiding this comment.
Send curate payload via stdin instead of argv
brvCurate passes the entire serialized turn as a positional CLI argument, so curation is constrained by OS command-line limits (ARG_MAX); long code snippets or verbose assistant replies can trigger spawn E2BIG and skip curation for exactly the large-context cases this plugin targets. Because afterTurn treats failures as best-effort, this becomes a silent memory-loss path rather than a hard failure, so the payload should be streamed via stdin (or another non-argv transport).
Useful? React with 👍 / 👎.
|
Nice direction overall — the I do think the Right now The key issue is that, in the current OpenClaw runtime, So when
For comparison, the built-in I think this should be fixed in one of two ways:
Given the rest of the PR, option 1 seems more consistent with the current design boundary: ByteRover handles long-term memory curation/retrieval, while OpenClaw continues to own short-term session compaction. Without that change, this PR seems to unintentionally disable compaction whenever |
|
Thanks for this @danhdoan, and for the insightful review and ensuing conversation about As for this PR, we're not accepting new plugins into this repository. It's bad for you because you need to wait for our review any time you want to update your plugin; it's bad for us because it adds large amounts of new code surface area that we shouldn't be responsible for maintaining. The better pattern is to create your own repository to host the plugin and publish it to NPM yourself. Then you can own the full maintenance lifecycle. Things are in the works to boost plugin visibility via a big update to clawhub that should land pretty soon. As a reference, check out lossless-claw, which is a context engine plugin that's hosted in an external repository. It's been very helpful for me to be able to maintain that independently of this project. |
|
Hi @jalehman, thanks for clarifying this. I agree with this decision and I will develop an external plugin as the next step. However, there is 1 more thing I should ask for your opinion first. Currently Happy to adjust my approach based on your feedback. |
|
Hi @jalehman, thanks for the feedback on the original PR, I've split out the upstream interface change into its own PR: #50848. It adds the optional Let me know if anything needs adjusting! |
Summary
extensions/byterover/, 1605 lines) that curates conversation turns viabrv curateand retrieves relevant knowledge viabrv query, injected assystemPromptAddition. Added optionalprompt?: stringto theassemble()interface (3 lines in core) so retrieval-augmented engines receive the current query directly. ByteRover provides persistent, stateful, long-term memory for AI agents and enables humans and agents to collaboratively manage project context through intelligent, cutting-edge curation and retrieval.LegacyContextEngine, message format, or API contracts. No new runtime dependencies in rootpackage.json. Thepromptparameter is optional — all existing engines are unaffected.Change Type
Scope
Linked Issue/PR
User-visible / Behavior Changes
byterovercontext engine plugin available underextensions/byterover/brvPath,cwd,queryTimeoutMs,curateTimeoutMsunderplugins.entries.byterover.configplugins.slots.contextEngine: "byterover"in~/.openclaw/openclaw.jsonConfiguration
Enable in
~/.openclaw/openclaw.json:{ "plugins": { "slots": { "contextEngine": "byterover" }, "entries": { "byterover": { "enabled": true, "config": { "brvPath": "/path/to/brv", "cwd": "/path/to/project-with-brv-initialized" } } } } }All
configfields are optional —brvPathdefaults to"brv"(resolved from PATH),cwddefaults toprocess.cwd(), timeouts have sensible defaults (12s query, 60s curate).Prerequisites
brv init)Security Impact (required)
NoNoNo— the plugin spawns a local CLI binary; brv daemon handles its own network communicationYes— spawnsbrvchild processesNo— context engines already access the same message data that compaction accessesmaxOutputChars) —runBrvkills the child if stdout exceeds limit to prevent memory exhaustion. Process cleanup on abort via AbortSignal + SIGKILL ensures no orphan processes. Metadata stripping prevents untrusted sender metadata from leaking into brv queries. POSIX--arg terminator prevents user text starting with-from being parsed as CLI flags.Repro + Verification
Environment
{ "plugins": { "slots": { "contextEngine": "byterover" }, "entries": { "byterover": { "enabled": true, "config": { "brvPath": "/path/to/brv", "cwd": "/path/to/project" } } } } }Steps
openclaw.jsonas shown aboveafterTurn curating N new messages→brv curatespawned with--detachassemble querying brv→brv queryreturns curated knowledge →assemble injecting systemPromptAddition (N chars).brvfolderExpected
afterTurncurates new messages viabrv curate --detach(non-blocking, ~ms handshake)assembleretrieves relevant context viabrv queryand injects it assystemPromptAdditionActual
Evidence
Test suite (54 tests, all passing)
Platform compatibility & manual verification
Manually verified end-to-end (plugin load → curate → query → context injection) on:
Human Verification (required)
openclaw.plugin.jsonReview Conversations
Compatibility / Migration
Yes— the plugin is opt-in. The only core change (prompt?: stringonassemble()) is optional and does not affect existing engines.Yes— newplugins.slots.contextEngineandplugins.entries.byteroverconfig fields. No changes to existing config.NoFailure Recovery (if this breaks)
"contextEngine": "byterover"fromplugins.slotsinopenclaw.json, or set"enabled": falseon the byterover entry. The runtime falls back toLegacyContextEngineimmediately.~/.openclaw/openclaw.json— remove the byterover plugin entry.assemble brv query timed outwarnings in logs — indicates brv daemon is slow or unresponsiveByteRover CLI not founderrors —brvPathnot set andbrvnot in gateway's PATHsystemPromptAdditionmay grow largeRisks and Mitigations
Risk:
brvchild process hangs or orphans on timeoutrunBrvtimeout as fallback for callers that don't pass a signal.Risk: Large
systemPromptAdditioninflates token usagemaxResponseCharsconfig option.Risk:
prompt?: stringaddition toassemble()interface could affect other enginesLegacyContextEngine) ignore it.Design details (expand for architecture deep-dive)
How it works
Lifecycle mapping
afterTurnbrv curate --detachassemblebrv querysystemPromptAddition(system prompt, not messages — no feedback loop risk).ingestafterTurnhandles batch ingestion of completed turns.compactcompacted: falsewithownsCompaction: false. ByteRover manages knowledge persistence independently through its own curation pipeline — it doesn't need to own OpenClaw's compaction lifecycle. The runtime's built-in auto-compaction handles message-array truncation as usual, while ByteRover's curated knowledge survives independently in its own store.Data flow
ByteRover operates as a two-phase async pipeline: curation happens in the background after each turn, retrieval happens synchronously before the next turn. This means the agent always has access to curated knowledge from previous turns without any latency penalty on the curation side.
Phase 1 — Retrieval (assemble, synchronous, blocks prompt)
Phase 2 — Curation (afterTurn, async, non-blocking)
Key design decisions
systemPromptAdditionover message injection: Retrieved context goes into the system prompt, not the message array. This avoids feedback loops where injected context would be re-curated on the next turn, and avoids inflating token counts in the message array.Metadata stripping: OpenClaw prepends structured metadata blocks (sender info, thread context, reply context) to user messages. These are stripped before both
brv queryandbrv curateso the CLI receives only substantive content. Sender name/timestamp are extracted and re-attributed cleanly (e.g.,[Alice @ 2026-03-10T14:30:00Z]: How do hooks work?).Curation prompt prefix:
afterTurnprepends filtering instructions that tell brv to skip trivial messages (greetings, "ok", "thanks") server-side, reducing noise in the knowledge base. This is a deliberate token-saving measure — without it, every "got it" and "sure" would be curated, stored, retrieved on future queries, and injected into the system prompt, burning tokens on noise. By filtering at the curation boundary, we keep the knowledge base lean and reducesystemPromptAdditionsize on every subsequentassemblecall.Short-query gate:
assembleskips brv for queries under 5 characters (after metadata stripping) — "ok", "hi", "yes" don't warrant a process spawn or a round-trip to the knowledge base. Same token-saving philosophy as the curation prefix, applied at the retrieval boundary: don't spend tokens retrieving context for queries that won't benefit from it.POSIX
--arg terminator: Bothbrv queryandbrv curateuse--before user text to prevent content starting with-from being parsed as CLI flags.AbortSignal cancellation:
assemblecreates an AbortController with a 10s deadline. The signal propagates throughbrvQuery→runBrv→ child process kill. This replaces aPromise.racepattern and ensures clean process cleanup on timeout.Settled guard:
runBrvuses asettledboolean to prevent double resolve/reject from competing event handlers (timer, signal abort, stdout overflow, spawn error, process close). Only the first event settles the promise.Fire-and-forget is a misnomer for
--detach: Despite the async nature, we stillawaitthebrvCuratecall. The--detachflag makes the daemon queue work asynchronously — the CLI itself exits immediately after the handshake (~ms). We await to capture the JSON response (queued status, task ID) and to surface ENOENT/crash errors.Why
prompt?was added toassemble()The existing
assemble()signature only provides the message history — but the incoming user message (the one that triggered this turn) is not yet part ofmessagesat assembly time. For a retrieval-augmented context engine like ByteRover, the current query is the most critical input: it determines what to retrieve. Without it, the engine would have to scan backwards through history and guess which message is the latest user query — fragile and unreliable for multi-turn conversations. This change makes the caller's intent explicit and gives any context engine direct access to the query that matters most. The parameter is optional, so all existing engines are unaffected.Files changed
New files (plugin —
extensions/byterover/)context-engine.tsByteRoverContextEngineclass implementing theContextEngineinterfacebrv-process.tsrunBrvspawn wrapper,brvCurate,brvQuery, NDJSON parsermessage-utils.tsindex.tsopenclaw.plugin.jsonbrvPath,cwd, timeouts)package.jsoncontext-engine.test.tscontext-engine.integration.test.tsbrv-process.test.tsmessage-utils.test.tsCore changes (minimal, 3 lines)
src/context-engine/types.tsprompt?: stringparameter toassemble()signaturesrc/agents/pi-embedded-runner/run/attempt.tsparams.promptthrough toassemble()callsrc/plugin-sdk/byterover.tssrc/plugin-sdk/subpaths.test.tspackage.jsonopenclaw/plugin-sdk/byterover