Watch
0
0
Fork
You've already forked hyperhive
0

swarm: a soft context limit per agent, as a percent of the window, applied at turn end

This commit is contained in:
damocles 2026-11-15 15:13:50 +01:00 • committed by mara
commit 1e03979836
2 changed files with 214 additions and 35 deletions

View file

@ -36,6 +36,7 @@ impl SetFlags {
effort: effort.or(current.effort),
subagent_models: self.subagent_models.or(current.subagent_models),
max_subagents: current.max_subagents,
soft_context_percent: current.soft_context_percent,
})
}
}
@ -130,14 +131,18 @@ fn render_configured(s: Option<&InferenceSettings>) -> String {
fn render(s: &InferenceSettings) -> String {
const UNSET: &str = "(not set; the agent's own)";
format!(
"model: {}\neffort: {}\nsubagent_models: {}\nmax_subagents: {}\n",
"model: {}\neffort: {}\nsubagent_models: {}\nmax_subagents: {}\nsoft_context_percent: {}\n",
s.model.as_deref().unwrap_or(UNSET),
s.effort.map_or(UNSET, Effort::as_str),
s.subagent_models
.as_ref()
.map_or_else(|| UNSET.to_owned(), |m| m.join(", ")),
s.max_subagents
.map_or_else(|| "(not set; no cap)".to_owned(), |n| n.to_string())
.map_or_else(|| "(not set; no cap)".to_owned(), |n| n.to_string()),
s.soft_context_percent.map_or_else(
|| "(not set; the harness's own watermark)".to_owned(),
|n| format!("{n}%")
)
)
}
@ -198,10 +203,12 @@ mod tests {
assert_eq!(merged.effort, Some(Effort::High));
}
/// `set` has no flag for the cap, and a stored one survives it.
/// `set` has no flag for the cap or the soft limit, and stored ones
/// survive it.
#[test]
fn set_keeps_the_stored_max_subagents() {
let current: InferenceSettings = serde_json::from_str(r#"{"max_subagents":3}"#).unwrap();
fn set_keeps_the_stored_cap_and_soft_limit() {
let current: InferenceSettings =
serde_json::from_str(r#"{"max_subagents":3,"soft_context_percent":20}"#).unwrap();
let merged = SetFlags {
model: Some("opus".to_owned()),
..flags()
@ -209,6 +216,7 @@ mod tests {
.merge(current)
.unwrap();
assert_eq!(merged.max_subagents, Some(3));
assert_eq!(merged.soft_context_percent, Some(20));
}
/// An effort the controller would answer 422 for fails before the request.
@ -259,15 +267,19 @@ mod tests {
serde_json::from_str(r#"{"configured":{},"effective":null}"#).unwrap();
assert_eq!(
render_configured(empty.configured.as_ref()).lines().count(),
4
5
);
}
#[test]
fn an_empty_value_renders_every_field_as_unset() {
let row = render(&serde_json::from_str("{}").unwrap());
assert_eq!(row.lines().count(), 4);
assert!(row.ends_with("max_subagents: (not set; no cap)\n"), "{row}");
assert_eq!(row.lines().count(), 5);
assert!(
row.ends_with("soft_context_percent: (not set; the harness's own watermark)\n"),
"{row}"
);
assert!(row.contains("max_subagents: (not set; no cap)\n"), "{row}");
assert!(
row.lines()
.take(3)

View file

@ -386,11 +386,13 @@ enum AgentVerb {
/// show what it last did. Fails when the swarm has never heard from the
/// agent.
Watch(AgentWatchArgs),
/// Show the model, effort, subagent models and subagent cap the swarm sets
/// for an agent, or change the model, effort and subagent models.
/// Show the model, effort, subagent models, subagent cap and soft context
/// limit the swarm sets for an agent, or change the model, effort and
/// subagent models.
///
/// `get` prints the subagent cap (`max_subagents`); `set` has no flag for
/// it and keeps a stored one. Only swarm-ui sets it.
/// `get` prints the subagent cap (`max_subagents`) and the soft context
/// limit (`soft_context_percent`); `set` has no flag for them and keeps
/// stored ones. Only swarm-ui sets them.
///
/// A field the swarm sets wins over the agent's own value (its agent.nix
/// or a `hyperhive-model`/`hyperhive-effort` file) from its next turn on;