Skip to content

feat: add interactivity, wire hooks and tool health into bare.rs - #32

Merged
bobrykov merged 3 commits into
masterfrom
feat/interactivity
May 26, 2026
Merged

feat: add interactivity, wire hooks and tool health into bare.rs#32
bobrykov merged 3 commits into
masterfrom
feat/interactivity

Conversation

@bobrykov

Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@bobrykov, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 44 minutes and 18 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e3bb9736-02ab-43c8-ae0f-dc299a15f9fb

📥 Commits

Reviewing files that changed from the base of the PR and between c49d776 and 8cb487f.

📒 Files selected for processing (1)
  • src/engine/bare.rs
📝 Walkthrough

Walkthrough

Adds interactivity-aware hooks and per-tool health tracking to BareLoop (feature-gated): introduces Interactivity and HookAction helpers, extends HookExecutor, wires optional hook_executor and health_registry into BareLoop, and integrates hooks at compaction, tool dispatch, and session lifecycle points with health recording.

Changes

Hook-based lifecycle interception and tool health tracking

Layer / File(s) Summary
Interactivity contract and HookAction helpers
src/hooks.rs
Adds Interactivity enum and HookAction::is_ask() predicate; expands tests for default/equality and is_ask behavior.
HookExecutor interactivity implementation
src/hooks/executor.rs
Stores interactivity in HookExecutor, adds constructors/builders (with_interactivity, interactivity), implements apply_interactivity to downgrade AskBlock in headless mode (preserving message), and routes pre-tool-use results through this logic; tests for modes and downgrade behavior.
BareLoop hook and health registry fields
src/engine/bare.rs
Adds feature-gated imports and optional hook_executor and health_registry fields, initializes them to None in all constructors, and exposes set_hook_executor and set_health_registry setters.
Session termination types and notifications
src/engine/bare.rs
Introduces SessionEndInfo and EndReason, reworks finalise/abort flows to call notify_session_end with structured info, and augments notify_session_start/notify_session_end to call hook executor (feature-gated).
Context compaction pre/post hook integration
src/engine/bare.rs
maybe_compact_context runs a pre-compaction hook check that can skip compaction and emits a post-compaction hook notification with compaction metrics and duration (feature-gated).
Tool dispatch pre/post hooks and health recording
src/engine/bare.rs
Inserts pre-tool-use hook gate that may short-circuit calls, invokes post-tool-use hooks after tool execution, and records success/failure and duration into ToolHealthRegistry; adds helper methods for mapping hook outcomes and emitting post-use context (feature-gated).
Tool health metadata annotation
src/engine/bare.rs
Conditionalizes ToolCallResult.duration attribute with #[cfg_attr(not(feature = "tool_health"), expect(dead_code))] so dead-code expectations depend on the tool_health feature.

Possibly related PRs

  • dch-labs/loopctl#31: Wires ToolHealthRegistry into BareLoop's tool-dispatch recording; closely related to this PR's health recording changes.
  • dch-labs/loopctl#30: Prior hooks work this PR extends (adds Interactivity and HookAction helpers and executor behavior).
  • dch-labs/loopctl#29: Changes context compaction flow that this PR augments with pre/post hook checks.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding interactivity support and integrating hooks and tool health features into bare.rs.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 50.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/interactivity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/engine/bare.rs`:
- Around line 1154-1160: PreCompactContext and PostCompactContext are being
populated with placeholder zeros (tokens_before, tokens_after, duration_ms);
update the compaction code that builds these contexts (the PreCompactContext and
PostCompactContext construction sites) to supply real values: compute
tokens_before and tokens_after using the same tokenizer/counting utility used
elsewhere in this module (or expose an Option to represent unknown), and compute
duration_ms by capturing Instant::now() before compaction and subtracting it
after to produce a millisecond duration; ensure you set
PostCompactContext.tokens_after and PostCompactContext.duration_ms (and remove
literal 0s) so hook consumers receive real metrics.
- Around line 1918-1933: SessionEndContext is being filled with zeroed totals
and SessionEndReason is inferred via fragile substring checks; instead thread a
structured end-metadata value from the run/abort paths into this code and use it
to populate SessionEndContext fields (total_turns, total_tokens, duration_secs)
and reason. Update the exit paths that currently call into the block (where
run/abort produce errors) to produce and pass a typed EndMetadata/SessionEndInfo
(including reason as an enum, counts, and duration), replace the
error.is_some_and substring logic with a direct match on that enum
(SessionEndReason) and set ctx from the provided metadata. Adjust any
callers/signatures that construct the SessionEndContext so they accept the
structured metadata rather than inferring from error text.
🪄 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: 17d257ae-2a23-4bcb-a431-6e6538f46342

📥 Commits

Reviewing files that changed from the base of the PR and between d50d7cc and da467a6.

📒 Files selected for processing (3)
  • src/engine/bare.rs
  • src/hooks.rs
  • src/hooks/executor.rs

Comment thread src/engine/bare.rs
Comment thread src/engine/bare.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
src/engine/bare.rs (2)

1168-1196: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

abort_session discards accumulated token counts.

When the session ends due to cancellation or max-turns exceeded, the budget containing accumulated tokens from previous turns is available at the call sites (lines 911–923) but not passed to this function. Hook consumers receiving SessionEndContext will see total_tokens: 0 even if turns were completed.

Consider threading &SessionBudget through abort_session to provide accurate token totals.

🔧 Proposed fix
     fn abort_session(
         &self,
         turn_count: usize,
         session_duration: Duration,
         reason: AbortReason,
+        budget: &SessionBudget,
     ) -> Result<SessionResult, AgentError> {
         let reason_str = match &reason {
             AbortReason::Cancelled => "Cancelled",
             AbortReason::MaxTurnsExceeded => "Max turns exceeded",
         };
         self.emit_session_stop(turn_count, session_duration, false, reason_str);
         let end_reason = match &reason {
             AbortReason::Cancelled => EndReason::Cancelled,
             AbortReason::MaxTurnsExceeded => EndReason::MaxTurns,
         };
         self.notify_session_end(&SessionEndInfo {
             success: false,
             reason: end_reason,
             total_turns: turn_count,
-            total_tokens: 0,
+            total_tokens: budget.input_tokens.saturating_add(budget.output_tokens),
             duration_secs: session_duration.as_secs(),
         });

Update call sites:

             if self.is_cancelled() {
                 return self.abort_session(
                     budget.turn_count,
                     start.elapsed(),
                     AbortReason::Cancelled,
+                    &budget,
                 );
             }
             if budget.turn_count >= max_turns {
                 return self.abort_session(
                     budget.turn_count,
                     start.elapsed(),
                     AbortReason::MaxTurnsExceeded,
+                    &budget,
                 );
             }
🤖 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/engine/bare.rs` around lines 1168 - 1196, The abort_session
implementation drops accumulated token counts by always setting total_tokens: 0;
change abort_session to accept a &SessionBudget (or &SessionBudget) parameter,
update its signature and all callers (the places that call abort_session from
the session loop) to pass the current SessionBudget, and use the budget's total
token value when constructing SessionEndInfo.total_tokens (and any other places
that report token usage, e.g., emit_session_stop if it should reflect tokens).
Ensure the AbortReason handling and returned AgentError behavior remain
identical while plumbing SessionBudget through.

2268-2269: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

duration cfg attribute does not account for hooks feature usage.

notify_post_tool_use_hooks (line 1707) reads tool_result.duration when the hooks feature is enabled. The current attribute expects dead code only when tool_health is disabled, but if hooks is enabled (without tool_health), the field is used and the expect(dead_code) lint will be unsatisfied.

🔧 Proposed fix
-    #[cfg_attr(not(feature = "tool_health"), expect(dead_code))]
+    #[cfg_attr(not(any(feature = "tool_health", feature = "hooks")), expect(dead_code))]
     duration: Duration,
🤖 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/engine/bare.rs` around lines 2268 - 2269, The duration field is currently
annotated with #[cfg_attr(not(feature = "tool_health"), expect(dead_code))] but
notify_post_tool_use_hooks (which reads tool_result.duration) is enabled when
the hooks feature is on, so the dead_code expectation must only apply when
neither tool_health nor hooks is enabled; update the cfg_attr on the duration
field to require both not(feature = "tool_health") and not(feature = "hooks")
before applying expect(dead_code) so tool_result.duration is considered used
when hooks is enabled (refer to the duration field and
notify_post_tool_use_hooks/tool_result.duration to locate the change).
🤖 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.

Outside diff comments:
In `@src/engine/bare.rs`:
- Around line 1168-1196: The abort_session implementation drops accumulated
token counts by always setting total_tokens: 0; change abort_session to accept a
&SessionBudget (or &SessionBudget) parameter, update its signature and all
callers (the places that call abort_session from the session loop) to pass the
current SessionBudget, and use the budget's total token value when constructing
SessionEndInfo.total_tokens (and any other places that report token usage, e.g.,
emit_session_stop if it should reflect tokens). Ensure the AbortReason handling
and returned AgentError behavior remain identical while plumbing SessionBudget
through.
- Around line 2268-2269: The duration field is currently annotated with
#[cfg_attr(not(feature = "tool_health"), expect(dead_code))] but
notify_post_tool_use_hooks (which reads tool_result.duration) is enabled when
the hooks feature is on, so the dead_code expectation must only apply when
neither tool_health nor hooks is enabled; update the cfg_attr on the duration
field to require both not(feature = "tool_health") and not(feature = "hooks")
before applying expect(dead_code) so tool_result.duration is considered used
when hooks is enabled (refer to the duration field and
notify_post_tool_use_hooks/tool_result.duration to locate the change).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3305e3ac-3068-48e6-b555-e692d4f4f9c4

📥 Commits

Reviewing files that changed from the base of the PR and between da467a6 and c49d776.

📒 Files selected for processing (1)
  • src/engine/bare.rs

@bobrykov
bobrykov merged commit 1ce5990 into master May 26, 2026
6 checks passed
@bobrykov
bobrykov deleted the feat/interactivity branch July 1, 2026 06:34
bobrykov added a commit that referenced this pull request Aug 18, 2026
feat: add interactivity, wire hooks and tool health into bare.rs
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