Add configurable hook context spill limits (#34393)

## What changed

- Add `additionalContextLimit` to command hook configuration for events that can emit `additionalContext`.
- Apply the limit independently to each hook's context before it is sent to the model. Unset values retain the 2,500-token default, while `0` disables spilling for that hook.
- Expose configured limits through app-server hook and requirements responses, and show them in the TUI hooks browser.

## Testing

- Cover JSON and TOML parsing, discovery and hashing, per-hook spilling behavior, app-server responses, and TUI rendering.

GitOrigin-RevId: 55ddc2dbbdf9067dceee3e1968c03617bbf6bcea
This commit is contained in:
Andrei Eternal authored and copyberry committed 2026-07-20 18:46:02 +00:00
1 parent bd92b056dd
commit e4836f998d
37 files changed
+679 -85

No files matched your search

@@ -492,6 +492,14 @@ impl HooksBrowserView {
Some(MAX_COMMAND_DETAIL_LINES),
));
lines.push(detail_line("Timeout", &format!("{}s", hook.timeout_sec)));
if let Some(limit) = hook.additional_context_limit {
let value = if limit == 0 {
"unlimited".to_string()
} else {
format!("limit: {limit} approximate tokens")
};
lines.push(detail_line("Context", &value));
}
lines.push(detail_line("Trust", hook_trust_label(hook.trust_status)));
lines
}
@@ -936,6 +944,7 @@ mod tests {
command: Some(command.to_string()),
timeout_sec: 30,
status_message: None,
additional_context_limit: None,
source_path: test_path_buf("/tmp/hooks.json").abs(),
source,
plugin_id: plugin_id.map(str::to_string),
@@ -1080,6 +1089,35 @@ mod tests {
assert_snapshot!("hooks_browser_handlers", render_lines(&view, /*width*/ 112));
}
#[test]
fn renders_handler_additional_context_limit() {
let (tx_raw, _rx) = unbounded_channel::<AppEvent>();
let mut configured_hook = hook(
"path:context-limit",
HookEventName::PreToolUse,
HookSource::User,
/*plugin_id*/ None,
"/tmp/pre-tool-use.sh",
/*enabled*/ true,
/*is_managed*/ false,
/*display_order*/ 0,
);
configured_hook.additional_context_limit = Some(0);
let mut view = HooksBrowserView::new(
vec![configured_hook],
Vec::new(),
Vec::new(),
AppEventSender::new(tx_raw),
);
view.handle_key_event(KeyEvent::from(KeyCode::Enter));
assert_snapshot!(
"hooks_browser_additional_context_limit",
render_lines(&view, /*width*/ 112)
);
}
#[test]
fn renders_untrusted_enabled_handler_as_inactive() {
let (tx_raw, _rx) = unbounded_channel::<AppEvent>();
@@ -0,0 +1,19 @@
---
source: tui/src/bottom_pane/hooks_browser_view.rs
expression: "render_lines(&view, 112)"
---
PreToolUse hooks
Turn hooks on or off. Your changes are saved automatically.
[x] Hook 1
Event PreToolUse
Matcher Bash
Source User config - /tmp/hooks.json
Command /tmp/pre-tool-use.sh
Timeout 30s
Context unlimited
Trust Trusted
Press space or enter to toggle; esc to go back