Skip to content

fix: pass onWarn callback to BusClient for app.log routing (#21) - #44

Merged
four-bytes-robby merged 2 commits into
mainfrom
fix/21-busclient-logger
Jun 17, 2026
Merged

fix: pass onWarn callback to BusClient for app.log routing (#21)#44
four-bytes-robby merged 2 commits into
mainfrom
fix/21-busclient-logger

Conversation

@four-bytes-robby

@four-bytes-robby four-bytes-robby commented Jun 17, 2026

Copy link
Copy Markdown
Member

Closes #21

Changes

  • BusPublisher.init() now accepts optional onWarn callback, passed through to BusClient.connect()
  • TBG plugin entry point defers busPublisher.init() to inside the Plugin callback, passing ctx.client.app.log as the warning logger
  • No more console.warn — bus warnings are routed through opencode's structured logging

Summary by cubic

Routes bus warnings to structured logs by passing an onWarn callback to BusClient, removing console.warn in favor of ctx.client.app.log. Persists the callback across reconnects and awaits bus initialization in the plugin.

  • Bug Fixes
    • BusPublisher.init(opts?) stores onWarn and forwards it to BusClient.connect() and on reconnects.
    • Bus is initialized and awaited inside the Plugin callback once ctx.client is available; warnings go to ctx.client.app.log with safer arg formatting (JSON for objects, String for others).

Written for commit 0416924. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

Release Notes

  • Improvements
    • Added optional warning callback support so the system can route warnings through the application’s logging.
    • Improved reliability during reconnections by preserving the previously configured warning handler.
    • Adjusted initialization timing for the token-budget protection plugin so warning logging is available once the client context is ready.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Walkthrough

BusPublisher.init gains an optional opts parameter carrying an onWarn callback that is forwarded to BusClient.connect. Reconnection logic preserves the stored handler. In FourTokenBudgetGuardPlugin, the busPublisher.init() call is moved from module load time into the plugin callback and is configured with an onWarn handler that routes warnings through ctx.client.app.log.

Changes

Warning handler wiring from BusPublisher to plugin

Layer / File(s) Summary
BusPublisher.init signature and onWarn storage
src/bus-publisher.ts
init accepts opts?: { onWarn?: (message: string, ...args: unknown[]) => void }, stores the callback on the instance, and passes opts?.onWarn to BusClient.connect alongside the existing timeout.
Reconnection preservation of onWarn
src/bus-publisher.ts
Reconnection calls init({ onWarn: this.onWarn }) instead of init() without arguments, ensuring the warning handler persists across reconnects.
FourTokenBudgetGuardPlugin deferred init with app.log handler
src/four-opencode-token-budget-guard.ts
busPublisher.init() is relocated inside FourTokenBudgetGuardPlugin (after ctx.client is available) and receives an onWarn callback that logs through ctx.client.app.log with stringified extra.details payload, suppressing logging failures. currentSessionID is declared in the same updated region.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~18 minutes

Possibly related issues

Possibly related PRs

Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR addresses warning routing via BusClient.connect but does not implement the core compaction-trigger feature [#21]: setting CC_COMPACTION_TRIGGER on soft/hard limits or policy violations. Implement the compaction-trigger feature by setting process.env.CC_COMPACTION_TRIGGER in chat.message hook and policy enforcement paths, add FOUR_TBG_COMPACTION_TRIGGER config, and document in HISTORY.md.
Out of Scope Changes check ❓ Inconclusive Changes to BusPublisher and warning callback routing appear aligned with supporting infrastructure for the compaction-trigger feature but are not explicitly scoped in issue #21. Clarify whether BusPublisher modifications are necessary infrastructure for the compaction-trigger implementation or should be addressed in a separate PR.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: enabling onWarn callback forwarding to BusClient for app.log routing.

Tip: You can configure your own custom pre-merge checks in the settings.

Finishing Touches
Generate docstrings
  • Create stacked PR
  • Commit on current branch
Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/21-busclient-logger
Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/21-busclient-logger

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/bus-publisher.ts (1)

54-57: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Reconnection loses the onWarn callback.

The reconnection logic calls this.init() without preserving the opts parameter. After a reconnection, bus warnings will no longer be routed through the application's structured logging system, silently breaking the feature introduced by this PR.

Store the opts parameter as a class field during the initial init() call, then reuse it during reconnection.

Proposed fix to preserve onWarn across reconnections
 export class BusPublisher {
   private bus: BusClient | null = null;
   private lastPublish = 0;
   private publishInterval = 500; // ms — debounce
   private reconnecting = false;
+  private initOpts?: { onWarn?: (message: string, ...args: unknown[]) => void };

   async init(opts?: { onWarn?: (message: string, ...args: unknown[]) => void }): Promise<void> {
+    this.initOpts = opts;
     try {
       this.bus = await BusClient.connect({ timeoutMs: 3000, onWarn: opts?.onWarn });
     } catch {
       this.bus = null;
     }
   }

   // ... in reconnection logic:
         setTimeout(async () => {
           this.reconnecting = false;
-          await this.init();
+          await this.init(this.initOpts);
         }, 5000);
Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/bus-publisher.ts` around lines 54 - 57, The reconnection logic in the
setTimeout callback is calling this.init() without passing the opts parameter,
which causes the onWarn callback to be lost after reconnection. Store the opts
parameter as a class field (e.g., this.opts) when init() is first called, then
modify the setTimeout callback in the reconnection block to pass this stored
opts value back to this.init() so that the onWarn callback is preserved across
reconnections.
src/four-opencode-token-budget-guard.ts (1)

80-81: ⚠️ Potential issue | 🔴 Critical

Plugin does not register the required chat.message Hook—currently uses event hook processing message.part.updated.

The coding guideline requires registering a chat.message Hook, but the plugin implementation at line 81 returns an event hook that processes individual message.part.updated events. This causes the documented limitation noted in ROADMAP.md: the hook fires per-chunk and lacks full message context, preventing policies that inspect assembled message content from triggering. The plugin should be updated to register the chat.message Hook or a pre-request hook variant (e.g., chat.message.before) that provides complete message context before token waste occurs.

Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/four-opencode-token-budget-guard.ts` around lines 80 - 81, The plugin
currently registers an event hook that processes message.part.updated events,
which fires per-chunk and lacks full message context. Replace the event hook
processing message.part.updated with a chat.message Hook (or a pre-request
variant like chat.message.before) to receive complete message context before
token consumption occurs. This change will allow policies to inspect the fully
assembled message content and enforce token budget constraints appropriately.

Source: Coding guidelines

Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/four-opencode-token-budget-guard.ts`:
- Line 40: The current implementation uses `args.map(String).join(" ")` to
format the details in the extra object, which produces unhelpful output like
"[object Object]" for complex objects. Replace the String coercion with
JSON.stringify to properly serialize and preserve the structure of complex
objects in the log details, making debugging information more informative.
- Around line 33-44: The busPublisher.init() call is not being awaited, which
creates a race condition where bus events published before initialization
completes may be dropped. Add the await keyword before busPublisher.init() to
ensure the initialization completes before the plugin continues processing. If
the containing function is not already declared as async, make it async to allow
the await to work properly.

---

Outside diff comments:
In `@src/bus-publisher.ts`:
- Around line 54-57: The reconnection logic in the setTimeout callback is
calling this.init() without passing the opts parameter, which causes the onWarn
callback to be lost after reconnection. Store the opts parameter as a class
field (e.g., this.opts) when init() is first called, then modify the setTimeout
callback in the reconnection block to pass this stored opts value back to
this.init() so that the onWarn callback is preserved across reconnections.

In `@src/four-opencode-token-budget-guard.ts`:
- Around line 80-81: The plugin currently registers an event hook that processes
message.part.updated events, which fires per-chunk and lacks full message
context. Replace the event hook processing message.part.updated with a
chat.message Hook (or a pre-request variant like chat.message.before) to receive
complete message context before token consumption occurs. This change will allow
policies to inspect the fully assembled message content and enforce token budget
constraints appropriately.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d4401af9-92c3-416d-8006-c932aec9a1b1

Commits

Reviewing files that changed from the base of the PR and between 9be25e3 and 5bd146c.

Files selected for processing (2)
  • src/bus-publisher.ts
  • src/four-opencode-token-budget-guard.ts

Comment thread src/four-opencode-token-budget-guard.ts Outdated
Comment thread src/four-opencode-token-budget-guard.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 2 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/four-opencode-token-budget-guard.ts Outdated
Comment thread src/four-opencode-token-budget-guard.ts Outdated
Comment thread src/four-opencode-token-budget-guard.ts Outdated
@four-bytes-robby
four-bytes-robby enabled auto-merge (squash) June 17, 2026 08:13

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/four-opencode-token-budget-guard.ts">

<violation number="1" location="src/four-opencode-token-budget-guard.ts:40">
P2: `JSON.stringify(a)` throws on circular objects. If a warning arg is circular, the entire `onWarn` callback throws, potentially breaking BusClient's internal warning handler.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

service: "tbg",
level: "warn",
message: msg,
extra: { details: args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(" ") }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: JSON.stringify(a) throws on circular objects. If a warning arg is circular, the entire onWarn callback throws, potentially breaking BusClient's internal warning handler.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/four-opencode-token-budget-guard.ts, line 40:

<comment>`JSON.stringify(a)` throws on circular objects. If a warning arg is circular, the entire `onWarn` callback throws, potentially breaking BusClient's internal warning handler.</comment>

<file context>
@@ -30,14 +30,14 @@ let currentSessionID = "";
           level: "warn",
           message: msg,
-          extra: { details: args.map(String).join(" ") }
+          extra: { details: args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(" ") }
         }
       }).catch(() => {});
</file context>
Suggested change
extra: { details: args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(" ") }
extra: { details: args.map(a => { try { return typeof a === 'object' ? JSON.stringify(a) : String(a); } catch { return String(a); } }).join(" ") }

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/bus-publisher.ts`:
- Around line 56-59: The reconnection logic in the setTimeout callback sets
this.reconnecting to false before the reconnect attempt completes, causing
permanent disconnection if init fails. Wrap the await this.init call in a
try-catch block and only set this.reconnecting to false after the init call
succeeds. If init fails in the catch block, handle the error appropriately (such
as logging it) to allow subsequent reconnection attempts to be scheduled
properly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e93716c-395d-4deb-9e39-44177b31d578

Commits

Reviewing files that changed from the base of the PR and between 5bd146c and 0416924.

Files selected for processing (2)
  • src/bus-publisher.ts
  • src/four-opencode-token-budget-guard.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/four-opencode-token-budget-guard.ts

Comment thread src/bus-publisher.ts
Comment on lines 56 to 59
setTimeout(async () => {
this.reconnecting = false;
await this.init();
await this.init({ onWarn: this.onWarn });
}, 5000);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reconnect attempts can stop permanently after a failed retry.

Line 57 resets this.reconnecting before the reconnect attempt finishes. If init fails at Line 58, this.bus remains null and publish exits early at Line 27, so no further retries are scheduled.

Proposed fix
-        setTimeout(async () => {
-          this.reconnecting = false;
-          await this.init({ onWarn: this.onWarn });
-        }, 5000);
+        const reconnect = async (): Promise<void> => {
+          await this.init({ onWarn: this.onWarn });
+          if (!this.bus) {
+            setTimeout(reconnect, 5000);
+            return;
+          }
+          this.reconnecting = false;
+        };
+        setTimeout(reconnect, 5000);
Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setTimeout(async () => {
this.reconnecting = false;
await this.init();
await this.init({ onWarn: this.onWarn });
}, 5000);
const reconnect = async (): Promise<void> => {
await this.init({ onWarn: this.onWarn });
if (!this.bus) {
setTimeout(reconnect, 5000);
return;
}
this.reconnecting = false;
};
setTimeout(reconnect, 5000);
Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/bus-publisher.ts` around lines 56 - 59, The reconnection logic in the
setTimeout callback sets this.reconnecting to false before the reconnect attempt
completes, causing permanent disconnection if init fails. Wrap the await
this.init call in a try-catch block and only set this.reconnecting to false
after the init call succeeds. If init fails in the catch block, handle the error
appropriately (such as logging it) to allow subsequent reconnection attempts to
be scheduled properly.

@four-bytes-robby
four-bytes-robby merged commit d8296b7 into main Jun 17, 2026
5 checks passed
@four-bytes-robby
four-bytes-robby deleted the fix/21-busclient-logger branch June 17, 2026 08:35
@four-bytes-robby
four-bytes-robby restored the fix/21-busclient-logger branch June 17, 2026 18:06
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.

[FEAT] Compaction-Trigger: curator-Compaction bei Budget-Überschreitung auslösen

1 participant