#1646

[FEATURE] Add /compact command for sub-agents (child agents)

Opentechnoconsulxopened 5d ago

Summary

The /compact command is currently only available/functional for the root agent. Sub-agents (child agents spawned via spawn_child_agent()) cannot trigger compaction themselves — they have no access to a /compact command.

This means sub-agents are helpless when their session grows too large. They can't proactively compact, can't control their context window, and will crash with overflow errors once the accumulated history exceeds the model limit.

Current Behavior

  1. The root agent can use /compact to manually trigger compaction.
  2. Automatic compaction is also triggered inside _run_cycle() (line 792 of strix/core/execution.py) when context gets close to the model's limit.
  3. However, sub-agents often receive massive inherited context from the parent via child_initial_input(), and this history is passed before the first _run_cycle compaction can trigger.
  4. Sub-agents have no /compact command to manually intervene.

Root Cause

1. Sub-agents receive unbounded parent history

In strix/core/inputs.py (lines 342–377):

def child_initial_input(
    *,
    name: str,
    child_id: str,
    parent_id: str,
    task: str,
    parent_history: list[Any],
) -> list[dict[str, Any]]:
    if parent_history:
        rendered = json.dumps(
            scrub_images_from_items(parent_history),
            ensure_ascii=False,
            default=str,
        )
        parts.append(
            "== Inherited context from parent (background only) ==\n"
            f"{rendered}\n"
            "== End of inherited context ==\n"
            "Use the above as background only; do not continue the "
            "parent's work. Your task follows.",
        )

This serializes the entire parent history with json.dumps() — no token counting, no truncation, no compaction. The scrub_images_from_items() call only removes image blocks; it does not trim text content or history length.

2. Automatic compaction only triggers inside _run_cycle, after children already have the full history

In strix/core/execution.py, _compact_session() is called at line 792 inside _run_cycle():

try:
    await _compact_session(agent, session, run_config, force=False)
except Exception:
    logger.exception("proactive compaction failed for %s", agent_id)

For child agents the flow is:

  1. spawn_child_agent() → child_initial_input(parent_history=...) — full history passed
  2. _start_child_runner() → run_agent_loop() → _run_until_lifecycle() → _run_cycle() — only here automatic compaction triggers

The full unbounded history is already in the child's session when _run_cycle starts. The compaction at line 792 runs after the damage is potentially done — and in many cases it runs after the LLM call has already been made with the oversized context.

Note: Manual /compact is available only to the root agent — sub-agents have no such command.

3. No manual control for sub-agents

Unlike the root agent, sub-agents have no way to manually trigger compaction via a /compact-style command. They must wait for automatic compaction, which may be too late.

Reproduction Scenario

  1. Parent agent runs many turns (e.g., file scanning, code analysis across many files).
  2. Parent session accumulates ~50K-100K+ tokens of history.
  3. Parent calls agent_spawn() / spawn_child_agent().
  4. Child receives the full parent_history as its initial input via child_initial_input().
  5. Child's first LLM request includes: full history + task prompt + tool definitions.
  6. If total tokens exceed the model's context window → context overflow error on the child's very first call.

Even without parent history, a sub-agent running many turns will eventually hit its context limit with no way to manually trigger compaction.

Proposed Solution

  • Expose the existing _compact_session() / maybe_compact() infrastructure as a /compact command for sub-agents, giving them the same manual control the root agent already has.
  • Before spawn_child_agent() passes parent_history to child_initial_input(), attempt a non-forced compaction of the parent session (non-critical; on failure continue with existing history).
  • As defense-in-depth, add a configurable cap on inherited context (e.g. STRIX_INHERIT_CONTEXT_MAX_TOKENS, default ~4000) and trim parent_history in child_initial_input() before serialization.

Related Issues / PRs

Impact

This affects any workflow where:

  • A parent agent has a long conversation history and spawns sub-agents
  • Sub-agents need to run many turns and risk hitting context limits
  • Small models or models with narrow context windows are used

Affected providers include any LiteLLM backend with strict context limits (e.g., Perplexity, llama.cpp, smaller OpenRouter models).


Question for maintainers: Would a maintainer confirm if this approach is acceptable before I open a PR?

Activity

0

No activity yet.