Skip to content

feat(config): support per-agent compaction overrides#69988

Closed
scottgl9 wants to merge 4 commits into
openclaw:mainfrom
scottgl9:scottgl9/per-agent-compaction-config
Closed

feat(config): support per-agent compaction overrides#69988
scottgl9 wants to merge 4 commits into
openclaw:mainfrom
scottgl9:scottgl9/per-agent-compaction-config

Conversation

@scottgl9

Copy link
Copy Markdown
Contributor

Summary

  • Problem: compaction overrides were supported only at agents.defaults.compaction, so all agents had to share the same compaction instructions and runtime settings.
  • Why it matters: specialized agents need different compaction guidance to preserve the right context during long-running sessions.
  • What changed: added optional agents.list[].compaction support, merged it over defaults, and threaded effective per-agent compaction config through embedded runtime compaction paths.
  • What did NOT change (scope boundary): default-only behavior remains unchanged when per-agent overrides are omitted; this PR does not redesign the broader config resolution model beyond compaction.

Change Type (select all)

  • Feature
  • Refactor required for the fix

Scope (select all touched areas)

  • Gateway / orchestration
  • API / contracts
  • UI / DX

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: compaction config existed only on the agent defaults surface, and runtime compaction paths read only agents.defaults.compaction.
  • Missing detection / guardrail: there was no config/runtime path for agent-scoped compaction resolution.
  • Contributing context (if known): upstream already had a mature compaction config shape, so the missing behavior was override placement and runtime wiring rather than compaction capability itself.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • src/agents/agent-scope.test.ts
    • src/agents/pi-settings.test.ts
    • src/agents/pi-embedded-runner/extensions.test.ts
  • Scenario the test should lock in:
    • per-agent compaction config merges over defaults
    • per-agent customInstructions wins over default customInstructions
    • runtime compaction setup consumes the effective agent-specific config
  • Why this is the smallest reliable guardrail:
    • these tests cover both config resolution and the embedded runtime handoff where compaction instructions are actually consumed
  • Existing test that already covers this (if any): config schema and runtime-config suites cover the broader config surface
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

  • OpenClaw now accepts optional agents.list[].compaction entries.
  • Per-agent compaction fields inherit from agents.defaults.compaction and override defaults when set.
  • agents.list[].compaction.customInstructions now lets each agent provide its own compaction guidance.

Diagram (if applicable)

Before:
[agent run] -> [agents.defaults.compaction only] -> [shared compaction behavior for every agent]

After:
[agent run] -> [agents.list[].compaction if present] -> [fallback to agents.defaults.compaction] -> [agent-specific compaction behavior]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: Linux
  • Runtime/container: local OpenClaw dev environment
  • Model/provider: N/A (config/runtime path change)
  • Integration/channel (if any): N/A
  • Relevant config (redacted): multi-agent config with agents.defaults.compaction plus agents.list[].compaction

Steps

  1. Configure agents.defaults.compaction.customInstructions.
  2. Add agents.list[].compaction.customInstructions for a specific agent.
  3. Run config validation and embedded compaction/runtime tests.

Expected

  • Config validates.
  • Effective compaction settings for that agent use the per-agent override.
  • Other agents continue inheriting defaults.

Actual

  • Verified locally.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
    • staged typecheck/lint/import-cycle guards passed
    • generated schema baseline updated and validated
    • runtime-config and agents test suites passed in the changed-file gate
    • per-agent compaction merge and runtime precedence covered by added tests
  • Edge cases checked:
    • per-agent override with inherited default fields
    • no per-agent override preserves prior defaults-only behavior
    • shared schema extraction did not leave runtime import cycles
  • What you did not verify:
    • manual end-to-end interactive compaction against a live chat session

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Risks and Mitigations

  • Risk: per-agent compaction schema could drift from defaults compaction schema over time.
    • Mitigation: this PR extracts the shared compaction schema into a dedicated config schema module and reuses it in both places.
  • Risk: runtime compaction paths could accidentally keep reading defaults-only config.
    • Mitigation: this PR threads agentId through the affected embedded runtime setup paths and adds tests around the effective runtime behavior.

@greptile-apps

greptile-apps Bot commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds optional per-agent compaction overrides (agents.list[].compaction) that merge over agents.defaults.compaction, extracts the compaction Zod schema into a shared module, and threads agentId through the embedded runtime compaction paths. The core design is sound and well-tested for the happy path, but there is a P1 regression in both resolveCompactionConfig (pi-settings.ts) and the inline resolution in extensions.ts: when agentId is provided but has no matching entry in agents.list, resolveAgentConfig returns undefined and the defaults compaction config is silently dropped — contrary to the behaviour of the parallel resolveAgentContextLimits helper which correctly uses ?? defaults.

Confidence Score: 4/5

Not ready to merge — the missing ?? defaults fallback in both compaction resolution paths is a P1 regression that silently drops defaults for agents not in the list.

Two P1 issues share the same root cause (missing fallback in resolveCompactionConfig and in the inline resolution in extensions.ts) and both require a one-line fix. The rest of the change is well-structured with good test coverage.

src/agents/pi-settings.ts and src/agents/pi-embedded-runner/extensions.ts both need the ?? agents.defaults.compaction fallback added.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/pi-settings.ts
Line: 44-49

Comment:
**Missing fallback to defaults when agent has no list entry**

When `agentId` is provided but no matching entry exists in `agents.list`, `resolveAgentConfig` returns `undefined`, so `?.compaction` resolves to `undefined` — silently dropping `agents.defaults.compaction` for any agent that runs without an explicit list entry. The parallel helper `resolveAgentContextLimits` shows the correct pattern with a `?? defaults` guard.

```suggestion
function resolveCompactionConfig(params: { cfg?: OpenClawConfig; agentId?: string }) {
  if (params.cfg && params.agentId) {
    return (
      resolveAgentConfig(params.cfg, params.agentId)?.compaction ??
      params.cfg?.agents?.defaults?.compaction
    );
  }
  return params.cfg?.agents?.defaults?.compaction;
}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/extensions.ts
Line: 92-95

Comment:
**Same missing fallback as `resolveCompactionConfig`**

If `agentId` is set but doesn't match any entry in `agents.list`, `resolveAgentConfig` returns `undefined` and `?.compaction` yields `undefined`, discarding the defaults compaction config. This is the same class of regression as in `pi-settings.ts`.

```suggestion
  const compactionCfg =
    params.cfg && params.agentId
      ? (resolveAgentConfig(params.cfg, params.agentId)?.compaction ??
          params.cfg?.agents?.defaults?.compaction)
      : params.cfg?.agents?.defaults?.compaction;
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/config/zod-schema.agent-compaction.ts
Line: 27-28

Comment:
**`modelFallbacks` silently added to defaults compaction schema**

The old inline schema in `zod-schema.agent-defaults.ts` did not include `modelFallbacks`. Extracting it into the shared `AgentCompactionSchema` introduces the field into `agents.defaults.compaction` as an accepted (but previously unknown) key, as confirmed by the generated schema diff. If this is intentional, a note in the PR or a type-level comment would clarify the intended surface; if not, `modelFallbacks` should be omitted from the shared schema or the defaults schema should exclude it explicitly.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: .github/workflows/ci-check-testbox.yml
Line: 24-25

Comment:
**Large CI resource bump with no explanation**

The runner jumps from `blacksmith-8vcpu` to `blacksmith-32vcpu` (4× CPU) and the timeout from 5 to 30 minutes (6×). The PR description doesn't mention this change. Is this intentional (e.g., to cover a newly-expanded test suite), or was it left over from local debugging? The testbox workflow is only triggered on `workflow_dispatch` or `.github/workflows/**` path changes, so the blast radius is limited — but an unintended 4× resource tier in a shared CI config is worth verifying explicitly.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(config): support per-agent compacti..." | Re-trigger Greptile

Comment thread src/agents/pi-settings.ts
Comment thread src/agents/pi-embedded-runner/extensions.ts
Comment thread src/config/zod-schema.agent-compaction.ts Outdated
Comment thread .github/workflows/ci-check-testbox.yml Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 230a78ea14

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/pi-settings.ts Outdated
Comment thread src/agents/pi-embedded-runner/extensions.ts
@aisle-research-bot

aisle-research-bot Bot commented Apr 22, 2026

Copy link
Copy Markdown

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Crash/DoS via unsafe object spread of optional compaction sub-objects in resolveAgentConfig
1. 🟡 Crash/DoS via unsafe object spread of optional compaction sub-objects in resolveAgentConfig
Property Value
Severity Medium
CWE CWE-476
Location src/agents/agent-scope-config.ts:130-150

Description

resolveAgentConfig() merges compaction.qualityGuard and compaction.memoryFlush using object spread on values that may be undefined (or null).

  • AgentCompactionSchema makes both qualityGuard and memoryFlush optional; when omitted they are undefined.
  • The merge condition uses typeof ... === "object" checks with ||, so it can enter the merge block when only one side is an object.
  • It then spreads both sides:
    • { ...agentDefaults?.compaction?.qualityGuard, ...entry.compaction.qualityGuard }
    • { ...agentDefaults?.compaction?.memoryFlush, ...entry.compaction.memoryFlush }
  • In JavaScript, object spread evaluates ToObject(source); spreading undefined/null throws TypeError: Cannot convert undefined or null to object.
  • Additionally, typeof null === "object" means a null value (if it can enter config) would incorrectly pass the type check and then crash on spread.

Impact: a malformed or partial agent configuration (e.g., defaults include qualityGuard but an agent entry has compaction: {} without qualityGuard, or vice-versa) can crash config resolution, potentially causing a service startup failure or runtime denial-of-service if configs are reloadable.

Recommendation

Avoid spreading possibly undefined/null values. Guard both sides and/or default to {}.

Example fix:

const qualityGuardDefaults =
  agentDefaults?.compaction?.qualityGuard && typeof agentDefaults.compaction.qualityGuard === "object"
    ? agentDefaults.compaction.qualityGuard
    : undefined;
const qualityGuardEntry =
  entry.compaction.qualityGuard && typeof entry.compaction.qualityGuard === "object"
    ? entry.compaction.qualityGuard
    : undefined;

qualityGuard: qualityGuardDefaults || qualityGuardEntry
  ? { ...(qualityGuardDefaults ?? {}), ...(qualityGuardEntry ?? {}) }
  : undefined,

const memoryFlushDefaults =
  agentDefaults?.compaction?.memoryFlush && typeof agentDefaults.compaction.memoryFlush === "object"
    ? agentDefaults.compaction.memoryFlush
    : undefined;
const memoryFlushEntry =
  entry.compaction.memoryFlush && typeof entry.compaction.memoryFlush === "object"
    ? entry.compaction.memoryFlush
    : undefined;

memoryFlush: memoryFlushDefaults || memoryFlushEntry
  ? { ...(memoryFlushDefaults ?? {}), ...(memoryFlushEntry ?? {}) }
  : undefined,

Also consider rejecting null in schema (e.g., ensure .nullable() is not allowed) and/or use z.object(...).optional() which already avoids null unless upstream coercion introduces it.


Analyzed PR: #69988 at commit eadf56e

Last updated on: 2026-04-22T05:57:25Z

@scottgl9

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback in commit e811e619a8 and pushed to the PR branch.

Resolved items:

Verification after the patch:

  • changed-file gate passed
  • typecheck passed
  • lint passed
  • runtime import cycle check passed
  • runtime-config suite passed
  • agents suite passed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e811e619a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/pi-embedded-runner/compact.ts Outdated
Comment thread src/agents/agent-scope-config.ts Outdated
@scottgl9

Copy link
Copy Markdown
Contributor Author

Direct follow-up for the two latest review threads:

Re-ran the changed-file verification after these fixes; it passed again (typecheck, lint, import-cycle guard, and agents tests).

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 63f5cbca58

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/pi-embedded-runner/compact.ts Outdated
@prtags

prtags Bot commented Apr 23, 2026

Copy link
Copy Markdown

Related work from PRtags group creative-gnat-d23p

Title: Open PR duplicate: per-agent compaction overrides config support

Number Title
#69987 feat(config): support per-agent compaction overrides
#69988* feat(config): support per-agent compaction overrides

* This PR

@1052326311

Copy link
Copy Markdown
Contributor

Hi @scottgl9 — I'm the original reporter of #57174 (the per-agent compaction request) and previously had #60807 attempting the same feature. clawsweeper correctly closed #60807 as superseded by this PR — yours is a cleaner, more focused implementation.

Sharing some production context that may help validation:

Use case that motivated #57174: mixed-tier agent hierarchy with Opus 4.6 (1M context) and GLM-5 (200k context) running side by side. With shared agents.defaults.compaction.reserveTokens=80000, the GLM agents compact every 1-3 turns while Opus agents barely touch threshold. Per-agent overrides let us set:

  • Opus agents: reserveTokens: 400000
  • GLM agents: reserveTokens: 40000

Test scenarios worth covering:

  1. Agent without compaction key → falls back to defaults.compaction exactly (no behavior change)
  2. Agent with partial compaction (e.g. only reserveTokens) → other fields inherit from defaults
  3. AgentId normalization in resolution path (we hit this on feat: per-agent compaction configuration overrides #60807 — was a Codex P1 finding)
  4. reserveTokensFloor propagation through pi-settings and post-compaction hooks (also a Codex finding on our PR)

Happy to test against our 17-agent production setup once this lands. Let me know if you want me to dry-run the merged config logic against any specific edge cases.

@clawsweeper

clawsweeper Bot commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close as superseded: this branch is now an outdated, conflicting partial implementation, while the active replacement PR owns the same broader per-agent compaction work with proof and focused remaining review.

Canonical path: Close this branch and converge the remaining per-agent compaction contract through #83637 and the canonical issue it closes.

So I’m closing this here and keeping the remaining discussion on #83637 and #52732.

Review details

Best possible solution:

Close this branch and converge the remaining per-agent compaction contract through #83637 and the canonical issue it closes.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main rejects agents.list[].compaction in the strict agent schema, and PR-head inspection shows the branch's remaining schema/runtime gaps.

Is this the best way to solve the issue?

No. The feature direction is valid, but this branch is no longer the best merge vehicle because the active replacement PR owns the broader contract and proof path.

Security review:

Security review cleared: No concrete security or supply-chain issue was found in the final PR surface; the concerns are functional config compatibility and runtime scope.

What I checked:

  • Live PR state: GitHub reports this PR open at head eadf56e with mergeStateStatus DIRTY; the body lists local checks but does not attach inspectable after-fix terminal output, logs, screenshots, recordings, or linked artifacts. (eadf56e7c978)
  • Current main defaults contract: Current main accepts defaults compaction keys including midTurnPrecheck, memoryFlush.model, truncateAfterCompaction, and maxActiveTranscriptBytes under agents.defaults.compaction. (src/config/zod-schema.agent-defaults.ts:165, 29f8715f05c8)
  • Current main still lacks public per-agent schema: Current main's strict AgentEntrySchema does not include a compaction property, so the feature request remains real but not implemented on main by this branch. (src/config/zod-schema.agent-runtime.ts:1004, 29f8715f05c8)
  • PR schema is narrower than current defaults: The PR's extracted AgentCompactionSchema omits current defaults keys such as midTurnPrecheck, memoryFlush.model, truncateAfterCompaction, and maxActiveTranscriptBytes, so merging it as-is would narrow existing accepted config. (src/config/zod-schema.agent-compaction.ts:29, eadf56e7c978)
  • PR accepts per-agent compaction before every consumer is effective: The PR adds compaction: AgentCompactionSchema to AgentEntrySchema, but the same head still leaves consumers such as compaction timeout and post-compaction sections reading defaults-only config. (src/config/zod-schema.agent-runtime.ts:833, eadf56e7c978)
  • Defaults-only timeout consumer on PR head: resolveCompactionTimeoutMs() on the PR head still reads cfg?.agents?.defaults?.compaction?.timeoutSeconds, so accepted per-agent timeout config would not be honored by that path. (src/agents/pi-embedded-runner/compaction-safety-timeout.ts:18, eadf56e7c978)

Likely related people:

  • vincentkoc: The central effective-agent resolver path traces to the agent-scope helper extraction that this feature would naturally extend. (role: agent-scope refactor author; confidence: high; commits: a372e4a15240; files: src/agents/agent-scope-config.ts, src/agents/agent-scope.ts)
  • Peter Steinberger: Recent history touched defaults compaction, memory flush, and release/current-main config surfaces that this PR must preserve. (role: recent config and compaction area contributor; confidence: medium; commits: 3a7a67b21873, e0dfc776bba8, 50a2481652b6; files: src/config/zod-schema.agent-defaults.ts, src/config/types.agent-defaults.ts, src/auto-reply/reply/memory-flush.ts)
  • asyncjason: The defaults-only timeout consumer relevant to this PR traces to the configurable compaction timeout change. (role: introduced related compaction timeout behavior; confidence: medium; commits: f77a6841317c; files: src/agents/pi-embedded-runner/compaction-safety-timeout.ts, src/config/zod-schema.agent-defaults.ts)
  • Alex Knight: Current-main blame for several central files points through the latest managed gateway update snapshot, so Alex is a useful recent-routing signal but not necessarily the feature origin. (role: recent baseline contributor; confidence: low; commits: c81271ee6e3a; files: src/config/zod-schema.agent-defaults.ts, src/agents/agent-scope-config.ts, src/agents/pi-settings.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 29f8715f05c8.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels May 20, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 20, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: support per-agent compaction overrides

2 participants