fix(compaction): configurable summarizer tool_choice; wider overflow detection

Port of upstream grok-build 0.2.105 + 0.2.118.

- CompactionToolChoice (auto default | none), resolved env
  KIGI_COMPACTION_TOOL_CHOICE > [features].compaction_tool_choice >
  remote settings. Some OpenAI-compatible servers reject
  tool_choice:"none" with tools attached, failing every long-session
  compaction; auto keeps them working (the summarization prompt already
  forbids tool use). Threaded from agent + subagent spawn through
  CompactionConfig into generate_session_compact; the Responses gate
  counts hosted tools too, so a hosted-only request still carries the
  configured choice.
- is_context_length_error recognizes the "current message ... exceeds
  budget" phrasing, so that overflow is Fatal instead of burning the
  retry budget on a deterministic failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KSy9S31pZNWXtRZNVJ1sDf
This commit is contained in:
ZacharyZhang-NYandClaude Fable 5 committed 2026-08-27 10:22:18 -04:00
1 parent 6a4bff0d45
commit 1ae803519e
18 files changed
+190 -13

No files matched your search

@@ -2171,6 +2171,19 @@ impl Config {
)
.with_segment_detail(self.resolve_compaction_detail())
}
/// Resolve summarizer `tool_choice`: env `KIGI_COMPACTION_TOOL_CHOICE` >
/// config > remote settings > default (`auto`).
pub(crate) fn resolve_compaction_tool_choice(
&self,
) -> crate::util::config::CompactionToolChoice {
crate::util::config::resolve_compaction_tool_choice_from(
env_string(crate::util::config::ENV_COMPACTION_TOOL_CHOICE).as_deref(),
self.features.compaction_tool_choice.as_deref(),
self.remote_settings
.as_ref()
.and_then(|r| r.compaction_tool_choice.as_deref()),
)
}
/// Resolve verbatim-input flag: env `KIGI_COMPACTION_VERBATIM_INPUT` > config > remote settings > default `true`.
pub(crate) fn resolve_compaction_verbatim_input(&self) -> bool {
BoolFlag::env("KIGI_COMPACTION_VERBATIM_INPUT")
@@ -3696,6 +3709,9 @@ pub struct Features {
/// Feed the summarizer the verbatim conversation instead of the lossy rewrite; `None` = defer to env/remote settings/default (true).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub compaction_verbatim_input: Option<bool>,
/// Summarizer `tool_choice` (`auto` | `none`); `None` = defer to env/remote settings/default (`auto`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub compaction_tool_choice: Option<String>,
/// Snapshot a completed subagent's isolated worktree into a durable git ref
/// and delete its directory (resume rehydrates from the ref). This is the
/// per-deployment rollout lever (set in managed_config.toml `[features]`).
@@ -2183,6 +2183,7 @@ impl MvpAgent {
.cfg
.borrow()
.resolve_compaction_verbatim_input();
let compaction_tool_choice = self.cfg.borrow().resolve_compaction_tool_choice();
let two_pass_enabled = self.cfg.borrow().is_two_pass_compaction_enabled();
let auto_update = self.cfg.borrow().cli.auto_update;
let client_type = *self.client_type.borrow();
@@ -2568,6 +2569,7 @@ impl MvpAgent {
system_prompt_label,
compaction_mode,
compaction_verbatim_input,
compaction_tool_choice,
two_pass_enabled,
buffering_settings,
origin_client.clone(),
@@ -1006,6 +1006,7 @@ pub(crate) async fn handle_subagent_request(
kigi_agent::DEFAULT_SYSTEM_PROMPT_LABEL.to_string(),
kigi_chat_state::CompactionMode::Summary,
ctx.resolve_compaction_verbatim_input(),
ctx.resolve_compaction_tool_choice(),
false,
None,
None,
@@ -397,6 +397,21 @@ impl SubagentSpawnContext {
.resolve()
.value
}
/// Subagent summarizer `tool_choice`, mirroring
/// `Config::resolve_compaction_tool_choice` (env > config > remote
/// settings > default `auto`).
pub fn resolve_compaction_tool_choice(&self) -> crate::util::config::CompactionToolChoice {
crate::util::config::resolve_compaction_tool_choice_from(
crate::agent::config::env_string(crate::util::config::ENV_COMPACTION_TOOL_CHOICE)
.as_deref(),
self.agent_config
.as_ref()
.and_then(|c| c.features.compaction_tool_choice.as_deref()),
self.remote_settings
.as_ref()
.and_then(|r| r.compaction_tool_choice.as_deref()),
)
}
/// Whether a completed subagent's worktree is snapshotted into a durable ref
/// and its directory deleted. Resolution mirrors the other subagent gates
/// (env > config > remote settings > default). Default `false` so it ships dark;